diff --git a/fine-third-default/fine-javax-cdi/src/javax/decorator/Decorator.java b/fine-third-default/fine-javax-cdi/src/javax/decorator/Decorator.java new file mode 100644 index 000000000..7e8c22439 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/decorator/Decorator.java @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.decorator; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.inject.Stereotype; + +/** + *

+ * Specifies that a class is a decorator. May be applied to a managed bean class. + *

+ * + *
+ * @Decorator 
+ * class TimestampLogger implements Logger { ... }
+ * 
+ * + *

+ * Decorators of a session bean must comply with the bean provider programming restrictions defined by the EJB specification. + * Decorators of a stateful session bean must comply with the rules for instance passivation and conversational state defined by + * the EJB specification. + *

+ * + * @see javax.decorator.Delegate @Delegate identifies the delegate injection point of a decorator. + * + * @author Gavin King + * @author Pete Muir + */ +@Target(TYPE) +@Retention(RUNTIME) +@Documented +@Stereotype +public @interface Decorator { +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/decorator/Delegate.java b/fine-third-default/fine-javax-cdi/src/javax/decorator/Delegate.java new file mode 100644 index 000000000..bae717cc7 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/decorator/Delegate.java @@ -0,0 +1,88 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.decorator; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + *

+ * Identifies the delegate injection point of a decorator. May be applied to a field, bean constructor parameter or initializer + * method parameter of a decorator bean class. + *

+ * + *
+ * @Decorator 
+ * class TimestampLogger implements Logger { 
+ *    @Inject @Delegate @Any Logger logger; 
+ *    ... 
+ * }
+ * 
+ * + *
+ * @Decorator 
+ * class TimestampLogger implements Logger { 
+ *    private Logger logger;
+ *    
+ *    @Inject
+ *    public TimestampLogger(@Delegate @Debug Logger logger) { 
+ *       this.logger=logger; 
+ *    } 
+ *    ... 
+ * }
+ * 
+ * + *

+ * A decorator must have exactly one delegate injection point. The delegate injection point must be an injected field, + * initializer method parameter or bean constructor method parameter. + *

+ * + *

+ * The container injects a delegate object to the delegate injection point. The delegate object implements the delegate type and + * delegates method invocations along the decorator stack. When the container calls a decorator during business method + * interception, the decorator may invoke any method of the delegate object. If a decorator invokes the delegate object at any + * other time, the invoked method throws an {@link java.lang.IllegalStateException}. + *

+ * + *
+ * @Decorator 
+ * class TimestampLogger implements Logger { 
+ *    @Inject @Delegate @Any Logger logger; 
+ *    
+ *    void log(String message) {
+ *       logger.log( timestamp() + ": " + message );
+ *    }
+ *    ...
+ * }
+ * 
+ * + * @see javax.decorator.Decorator @Decorator specifies that a class is a decorator. + * + * @author Gavin King + * @author Pete Muir + */ +@Target({ FIELD, PARAMETER }) +@Retention(RUNTIME) +@Documented +public @interface Delegate { +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/decorator/package-info.java b/fine-third-default/fine-javax-cdi/src/javax/decorator/package-info.java new file mode 100644 index 000000000..2bf95b183 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/decorator/package-info.java @@ -0,0 +1,97 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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. + */ +/** + *

Annotations relating to decorators.

+ * + *

A decorator implements one or more bean types and + * intercepts business method invocations of + * {@linkplain javax.enterprise.inject beans} which + * implement those bean types. These bean types are called + * decorated types.

+ * + *

A decorator is a managed bean annotated {@link + * javax.decorator.Decorator @Decorator}.

+ * + *

Decorators are superficially similar to interceptors, + * but because they directly implement operations with business + * semantics, they are able to implement business logic and, + * conversely, unable to implement the cross-cutting concerns + * for which interceptors are optimized. Decorators are called + * after interceptors.

+ * + *

Decorated types

+ * + *

The set of decorated types of a decorator includes all + * bean types of the managed bean that are Java interfaces, + * except for {@link java.io.Serializable}. The decorator bean + * class and its superclasses are not decorated types of the + * decorator. The decorator class may be abstract.

+ * + *

A decorator intercepts every method:

+ * + * + *

A decorator may be an abstract class, and is not required to + * implement every method of every decorated type.

+ * + *

Delegate injection points

+ * + *

All decorators have a + * {@linkplain javax.decorator.Delegate delegate injection point}. + * A delegate injection point is an injection point of the bean + * class annotated {@link javax.decorator.Delegate @Delegate}.

+ * + *

The type of the delegate injection point must implement or + * extend every decorated type. A decorator is not required to + * implement the type of the delegate injection point.

+ * + *

Enabled decorators

+ * + *

By default, a bean archive has no enabled decorators. A + * decorator must be explicitly enabled by listing its bean class + * under the <decorators> element of the + * beans.xml file of the bean archive. The order of the + * decorator declarations determines the decorator ordering. + * Decorators which occur earlier in the list are called first.

+ * + *

A decorator is bound to a bean if:

+ * + * + * + *

If a managed bean class is declared final, it may not have + * decorators. If a managed bean has a non-static, non-private, + * final method, it may not have any decorator which implements + * that method.

+ * + *

A decorator instance is a + * {@linkplain javax.enterprise.context.Dependent dependent object} + * of the object it decorates.

+ * + * @see javax.enterprise.inject + * + * @see javax.decorator.Decorator + * @see javax.decorator.Delegate + * + */ +package javax.decorator; + diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ApplicationScoped.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ApplicationScoped.java new file mode 100644 index 000000000..361893f09 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ApplicationScoped.java @@ -0,0 +1,104 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.util.AnnotationLiteral; + +/** + *

+ * Specifies that a bean is application scoped. + *

+ *

+ * While ApplicationScoped must be associated with the built-in application context required by the specification, + * third-party extensions are + * allowed to also associate it with their own context. Behavior described below is only related to the built-in application context. + *

+ * + *

+ * The application scope is active: + *

+ * + * + * + *

+ * The application context is shared between all servlet requests, web service invocations, EJB remote method invocations, EJB + * asynchronous method invocations, EJB timeouts and message deliveries to message-driven beans that execute within the same + * application. + *

+ *

+ * The application context is destroyed when the application is shut down. + *

+ * + *

+ * An event with qualifier @Initialized(ApplicationScoped.class) is fired when the application context is initialized + * and an event with qualifier @Destroyed(ApplicationScoped.class) when the application context is destroyed. + * The event payload is: + *

+ * + * + * + * @author Gavin King + * @author Pete Muir + * @author Antoine Sabot-Durand + */ + +@Target({ TYPE, METHOD, FIELD }) +@Retention(RUNTIME) +@Documented +@NormalScope +@Inherited +public @interface ApplicationScoped { + + /** + * Supports inline instantiation of the {@link ApplicationScoped} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ + public final static class Literal extends AnnotationLiteral implements ApplicationScoped { + + public static final Literal INSTANCE = new Literal(); + + private static final long serialVersionUID = 1L; + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/BeforeDestroyed.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/BeforeDestroyed.java new file mode 100644 index 000000000..42f24ce45 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/BeforeDestroyed.java @@ -0,0 +1,84 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Qualifier; + +/** + * An event with this qualifier is fired when a context is about to be destroyed, i.e. before the actual destruction. + * + * @author Pete Muir + * @author Martin Kouba + * @see Initialized + * @see Destroyed + * @since 2.0 + */ +@Qualifier +@Target({ TYPE, METHOD, PARAMETER, FIELD }) +@Retention(RUNTIME) +@Documented +public @interface BeforeDestroyed { + + /** + * The scope for which to observe destruction + */ + Class value(); + + /** + * Supports inline instantiation of the {@link BeforeDestroyed} qualifier. + */ + public final static class Literal extends AnnotationLiteral implements BeforeDestroyed { + + public static final Literal REQUEST = of(RequestScoped.class); + + public static final Literal CONVERSATION = of(ConversationScoped.class); + + public static final Literal SESSION = of(SessionScoped.class); + + public static final Literal APPLICATION = of(ApplicationScoped.class); + + private static final long serialVersionUID = 1L; + + private final Class value; + + public static Literal of(Class value) { + return new Literal(value); + } + + private Literal(Class value) { + this.value = value; + } + + public Class value() { + return value; + } + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/BusyConversationException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/BusyConversationException.java new file mode 100644 index 000000000..5a08b4446 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/BusyConversationException.java @@ -0,0 +1,59 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +/** + *

+ * Indicates that the container has rejected a request because a concurrent request is associated with the same conversation + * context. + *

+ * + *

+ * The container ensures that a long-running conversation may be associated with at most one request at a time, by blocking or + * rejecting concurrent requests. If the container rejects a request, it must associate the request with a new transient + * conversation and throw an exception of type BusyConversationException from the restore view phase of the JSF + * lifecycle. + *

+ * + * @see javax.enterprise.context.ConversationScoped + * + * @author Pete Muir + * @author Gavin King + */ + +public class BusyConversationException extends ContextException { + + private static final long serialVersionUID = -3599813072560026919L; + + public BusyConversationException() { + super(); + } + + public BusyConversationException(String message) { + super(message); + } + + public BusyConversationException(Throwable cause) { + super(cause); + } + + public BusyConversationException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ContextException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ContextException.java new file mode 100644 index 000000000..dfe4ee139 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ContextException.java @@ -0,0 +1,48 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +/** + *

+ * Indicates a problem relating to context management. + *

+ * + * @author Pete Muir + * @author Shane Bryzak + */ +public class ContextException extends RuntimeException { + + private static final long serialVersionUID = -3599813072560026919L; + + public ContextException() { + super(); + } + + public ContextException(String message) { + super(message); + } + + public ContextException(Throwable cause) { + super(cause); + } + + public ContextException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ContextNotActiveException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ContextNotActiveException.java new file mode 100644 index 000000000..595b8ae63 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ContextNotActiveException.java @@ -0,0 +1,52 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +/** + *

+ * Indicates that a context is not active. + *

+ * + * @see javax.enterprise.context.spi.Context + * + * @author Pete Muir + * @author Shane Bryzak + * @author Gavin King + */ + +public class ContextNotActiveException extends ContextException { + + private static final long serialVersionUID = -3599813072560026919L; + + public ContextNotActiveException() { + super(); + } + + public ContextNotActiveException(String message) { + super(message); + } + + public ContextNotActiveException(Throwable cause) { + super(cause); + } + + public ContextNotActiveException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Conversation.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Conversation.java new file mode 100644 index 000000000..d833e8b58 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Conversation.java @@ -0,0 +1,113 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +/** + *

+ * Allows the application to manage the {@linkplain javax.enterprise.context.ConversationScoped conversation context} by marking + * the current conversation as transient or long-running, specifying a conversation identifier, or setting the conversation + * timeout. + *

+ * + *

+ * An instance may be injected: + *

+ * + *
+ * @Inject
+ * Conversation conversation;
+ * 
+ * + *

+ * The conversation timeout is a hint to the container that a conversation should not be destroyed if it has been active within + * the last given interval in milliseconds. + *

+ * + * @see javax.enterprise.context.ConversationScoped @ConversationScoped + * + * @author Pete Muir + * @author Gavin King + * + */ +public interface Conversation { + + /** + *

+ * Mark the current transient conversation long-running. A conversation identifier is generated by the container. + *

+ * + * @throws IllegalStateException if the current conversation is already marked long-running. + */ + public void begin(); + + /** + *

+ * Mark the current transient conversation long-running, with a specified identifier. + *

+ * + * @param id conversation id + * @throws IllegalStateException if the current conversation is already marked long-running. + * @throws IllegalArgumentException if a conversation with the specified identifier already exists. + */ + public void begin(String id); + + /** + *

+ * Marks the current long-running conversation transient. + *

+ * + * @throws IllegalStateException if the current conversation is already marked transient. + */ + public void end(); + + /** + *

+ * Get the identifier of the current long-running conversation. + *

+ * + * @return the identifier of the current long-running conversation, or a null value if the current conversation is + * transient. + */ + public String getId(); + + /** + *

+ * Get the timeout of the current conversation. + *

+ * + * @return the current timeout in milliseconds. + */ + public long getTimeout(); + + /** + *

+ * Set the timeout of the current conversation. + *

+ * + * @param milliseconds the new timeout in milliseconds. + */ + public void setTimeout(long milliseconds); + + /** + *

+ * Determine if the conversation is marked transient or long-running. + *

+ * + * @return true if the conversation is marked transient, or falseif it is marked long-running. + */ + public boolean isTransient(); +} \ No newline at end of file diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ConversationScoped.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ConversationScoped.java new file mode 100644 index 000000000..6cc9919bf --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/ConversationScoped.java @@ -0,0 +1,159 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.util.AnnotationLiteral; + +/** + *

+ * Specifies that a bean is conversation scoped. + *

+ *

+ * While ConversationScoped must be associated with the built-in conversation context required by the specification, + * third-party extensions are + * allowed to also associate it with their own context. Behavior described below is only related to the built-in conversation context. + *

+ *

+ * The conversation scope is active: + *

+ * + *

+ * An event with qualifier @Initialized(ConversationScoped.class) is fired when the conversation context is initialized + * and an event with qualifier @Destroyed(ConversationScoped.class) is fired when the conversation is destroyed. + * The event payload is: + *

+ * + * + *

+ * The conversation context provides access to state associated with a particular conversation. Every Servlet request + * has an associated conversation. This association is managed automatically by the container according to the following rules: + *

+ * + * + *

+ * + *

+ * Any conversation is in one of two states: transient or long-running. + *

+ * + * + * + *

+ * All long-running conversations have a string-valued unique identifier, which may be set by the application when the + * conversation is marked long-running, or generated by the container. + *

+ * + *

+ * If the conversation associated with the current Servlet request is in the transient state at the end of a Servlet + * request, it is destroyed, and the conversation context is also destroyed. + *

+ * + *

+ * If the conversation associated with the current Servlet request is in the long-running state at the end of a Servlet + * request, it is not destroyed. The long-running conversation associated with a request may be propagated to any Servlet + * request via use of a request parameter named cid containing the unique identifier of the conversation. In this + * case, the application must manage this request parameter. + *

+ * + *

+ * If the current Servlet request is a JSF request, and the conversation is in long-running state, it is propagated + * according to the following rules: + *

+ * + * + * + *

+ * When no conversation is propagated to a Servlet request, or if a request parameter named conversationPropagation has + * the value none the request is associated with a new transient conversation. + * All long-running conversations are scoped to a particular HTTP servlet session and may not cross session boundaries. + * In the following cases, a propagated long-running conversation cannot be restored and re-associated with the request: + *

+ * + * + * + * @see javax.enterprise.context.Conversation + * @see javax.enterprise.context.NonexistentConversationException + * @see javax.enterprise.context.BusyConversationException + * + * @author Gavin King + * @author Pete Muir + * @author Antoine Sabot-Durand + */ + +@Target({ TYPE, METHOD, FIELD }) +@Retention(RUNTIME) +@Documented +@NormalScope(passivating = true) +@Inherited +public @interface ConversationScoped { + + /** + * Supports inline instantiation of the {@link ConversationScoped} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ + public final static class Literal extends AnnotationLiteral implements ConversationScoped { + + public static final Literal INSTANCE = new Literal(); + + private static final long serialVersionUID = 1L; + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Dependent.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Dependent.java new file mode 100644 index 000000000..68691678c --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Dependent.java @@ -0,0 +1,118 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.context.spi.Contextual; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Scope; + +/** + *

+ * Specifies that a bean belongs to the dependent pseudo-scope. + *

+ * + *

+ * Beans declared with scope @Dependent behave differently to beans with other built-in scope types. When a bean is + * declared to have scope @Dependent: + *

+ * + *
    + *
  • No injected instance of the bean is ever shared between multiple injection points.
  • + *
  • Any instance of the bean injected into an object that is being created by the container is bound to the lifecycle of the + * newly created object.
  • + *
  • When a Unified EL expression in a JSF or JSP page that refers to the bean by its EL name is evaluated, at most one + * instance of the bean is instantiated. This instance exists to service just a single evaluation of the EL expression. It is + * reused if the bean EL name appears multiple times in the EL expression, but is never reused when the EL expression is + * evaluated again, or when another EL expression is evaluated.
  • + *
  • Any instance of the bean that receives a producer method, producer field, disposer method or observer method invocation + * exists to service that invocation only.
  • + *
  • Any instance of the bean injected into method parameters of a disposer method or observer method exists to service the + * method invocation only.
  • + *
+ * + *

+ * Every invocation of the {@link javax.enterprise.context.spi.Context#get(Contextual, CreationalContext)} operation of the + * context object for the @Dependent scope returns a new instance of the given bean. + *

+ * + *

+ * Every invocation of the {@link javax.enterprise.context.spi.Context#get(Contextual)} operation of the context object for the + * @Dependent scope returns a null value. + *

+ * + *

+ * The @Dependent scope is always active. + *

+ * + *

+ * Many instances of beans with scope @Dependent belong to some other bean or Java EE component class instance and are + * called dependent objects. + *

+ * + *
    + *
  • Instances of decorators and interceptors are dependent objects of the bean instance they decorate.
  • + *
  • An instance of a bean with scope @Dependent injected into a field, bean constructor or initializer method is a + * dependent object of the bean or Java EE component class instance into which it was injected.
  • + *
  • An instance of a bean with scope @Dependent injected into a producer method is a dependent object of the + * producer method bean instance that is being produced.
  • + *
  • An instance of a bean with scope @Dependent obtained by direct invocation of an + * {@link javax.enterprise.inject.Instance} is a dependent object of the instance of {@link javax.enterprise.inject.Instance}.
  • + *
+ * + *

+ * When the container destroys an instance of a bean or of any Java EE component class supporting injection, the container + * destroys all its dependent objects, after the @PreDestroy callback completes and after the servlet + * destroy() method is called. + *

+ * + * @author Gavin King + * @author Pete Muir + */ + +@Target({ METHOD, TYPE, FIELD }) +@Retention(RUNTIME) +@Documented +@Scope +@Inherited +public @interface Dependent { + + /** + * Supports inline instantiation of the {@link Dependent} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ + public final static class Literal extends AnnotationLiteral implements Dependent { + + public static final Literal INSTANCE = new Literal(); + + private static final long serialVersionUID = 1L; + + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Destroyed.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Destroyed.java new file mode 100644 index 000000000..d2af8a6dd --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Destroyed.java @@ -0,0 +1,85 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Qualifier; + +/** + * An event with this qualifier is fired when a context is destroyed, i.e. after the actual destruction. + * + * @author Pete Muir + * @see Initialized + * @see BeforeDestroyed + * @since 1.1 + */ +@Qualifier +@Target({ TYPE, METHOD, PARAMETER, FIELD }) +@Retention(RUNTIME) +@Documented +public @interface Destroyed { + + /** + * The scope for which to observe destruction + */ + Class value(); + + /** + * Supports inline instantiation of the {@link Destroyed} qualifier. + * + * @author Martin Kouba + */ + public final static class Literal extends AnnotationLiteral implements Destroyed { + + public static final Literal REQUEST = of(RequestScoped.class); + + public static final Literal CONVERSATION = of(ConversationScoped.class); + + public static final Literal SESSION = of(SessionScoped.class); + + public static final Literal APPLICATION = of(ApplicationScoped.class); + + private static final long serialVersionUID = 1L; + + private final Class value; + + public static Literal of(Class value) { + return new Literal(value); + } + + private Literal(Class value) { + this.value = value; + } + + public Class value() { + return value; + } + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Initialized.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Initialized.java new file mode 100644 index 000000000..78492922d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/Initialized.java @@ -0,0 +1,85 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Qualifier; + +/** + * An event with this qualifier is fired when a context is initialized, i.e. ready for use. + * + * @author Pete Muir + * @see BeforeDestroyed + * @see Destroyed + * @since 1.1 + */ +@Qualifier +@Target({ TYPE, METHOD, PARAMETER, FIELD }) +@Retention(RUNTIME) +@Documented +public @interface Initialized { + + /** + * The scope for which to observe initialization + */ + Class value(); + + /** + * Supports inline instantiation of the {@link Initialized} qualifier. + * + * @author Martin Kouba + */ + public final static class Literal extends AnnotationLiteral implements Initialized { + + public static final Literal REQUEST = of(RequestScoped.class); + + public static final Literal CONVERSATION = of(ConversationScoped.class); + + public static final Literal SESSION = of(SessionScoped.class); + + public static final Literal APPLICATION = of(ApplicationScoped.class); + + private static final long serialVersionUID = 1L; + + private final Class value; + + public static Literal of(Class value) { + return new Literal(value); + } + + private Literal(Class value) { + this.value = value; + } + + public Class value() { + return value; + } + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/NonexistentConversationException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/NonexistentConversationException.java new file mode 100644 index 000000000..252e53fab --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/NonexistentConversationException.java @@ -0,0 +1,56 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +/** + *

+ * Indicates that the conversation context could not be restored. + *

+ * + *

+ * If the propagated conversation cannot be restored, the container must associate the request with a new transient conversation + * and throw an exception of type NonexistentConversationException. + *

+ * + * @see javax.enterprise.context.ConversationScoped + * + * @author Pete Muir + * @author Gavin King + */ + +public class NonexistentConversationException extends ContextException { + + private static final long serialVersionUID = -3599813072560026919L; + + public NonexistentConversationException() { + super(); + } + + public NonexistentConversationException(String message) { + super(message); + } + + public NonexistentConversationException(Throwable cause) { + super(cause); + } + + public NonexistentConversationException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/NormalScope.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/NormalScope.java new file mode 100644 index 000000000..934ae8897 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/NormalScope.java @@ -0,0 +1,56 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + *

+ * Specifies that an annotation type is a normal scope type. + *

+ * + * @author Gavin King + * @author Pete Muir + * + * @see javax.inject.Scope @Scope is used to declare pseudo-scopes. + */ +@Target(ANNOTATION_TYPE) +@Retention(RUNTIME) +@Documented +public @interface NormalScope { + + /** + *

+ * Determines whether the normal scope type is a passivating scope. + *

+ * + *

+ * A bean is called passivation capable if the container is able to temporarily transfer the state of any idle instance to + * secondary storage. A passivating scope requires that beans with the scope are passivation capable. + *

+ * + * @return true if the scope type is a passivating scope type + */ + boolean passivating() default false; + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/RequestScoped.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/RequestScoped.java new file mode 100644 index 000000000..abdf7f17d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/RequestScoped.java @@ -0,0 +1,107 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.util.AnnotationLiteral; + +/** + *

+ * Specifies that a bean is request scoped. + *

+ *

+ * While RequestScoped must be associated with the built-in request context required by the specification, + * third-party extensions are + * allowed to also associate it with their own context. Behavior described below is only related to the built-in request context. + *

+ * + *

+ * The request scope is active: + *

+ * + *
    + *
  • during the service() method of any servlet in the web application, during the doFilter() method of any + * servlet filter and when the container calls any ServletRequestListener or AsyncListener,
  • + *
  • during any Java EE web service invocation,
  • + *
  • during any remote method invocation of any EJB, during any asynchronous method invocation of any EJB, during any call to + * an EJB timeout method and during message delivery to any EJB message-driven bean, and
  • + *
  • during @PostConstruct callback of any bean.
  • + *
+ * + *

+ * The request context is destroyed: + *

+ * + *
    + *
  • at the end of the servlet request, after the service() method, all doFilter() methods, and all + * requestDestroyed() and onComplete() notifications return,
  • + *
  • after the web service invocation completes,
  • + *
  • after the EJB remote method invocation, asynchronous method invocation, timeout or message delivery completes if it + * did not already exist when the invocation occurred, or
  • + *
  • after the @PostConstruct callback completes, if it did not already exist when the @PostConstruct + * callback occurred.
  • + *
+ * + *

+ * An event with qualifier @Initialized(RequestScoped.class) is fired when the request context is initialized and an + * event + * with qualifier @Destroyed(RequestScoped.class) when the request context is destroyed. The event payload is: + *

+ * + *
    + *
  • the ServletRequest if the context is initialized or destroyed due to a servlet request, or
  • + *
  • the ServletRequest if the context is initialized or destroyed due to a web service invocation, or
  • + *
  • any java.lang.Object for other types of request.
  • + *
+ * + * @author Gavin King + * @author Pete Muir + * @author Antoine Sabot-Durand + */ + +@Target({ TYPE, METHOD, FIELD }) +@Retention(RUNTIME) +@Documented +@NormalScope +@Inherited +public @interface RequestScoped { + + /** + * Supports inline instantiation of the {@link RequestScoped} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ + public final static class Literal extends AnnotationLiteral implements RequestScoped { + + public static final Literal INSTANCE = new Literal(); + + private static final long serialVersionUID = 1L; + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/SessionScoped.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/SessionScoped.java new file mode 100644 index 000000000..9af9dc7ab --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/SessionScoped.java @@ -0,0 +1,98 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.util.AnnotationLiteral; + +/** + *

+ * Specifies that a bean is session scoped. + *

+ *

+ * While SessionScoped must be associated with the built-in session context required by the specification, + * third-party extensions are + * allowed to also associate it with their own context. Behavior described below is only related to the built-in session context. + *

+ *

+ * The session scope is active: + *

+ * + *
    + *
  • during the service() method of any servlet in the web application, + *
  • during the doFilter() method of any servlet filter, and
  • + *
  • when the container calls any HttpSessionListener, AsyncListener or + * ServletRequestListener.
  • + *
+ * + *

+ * The session context is shared between all servlet requests that occur in the same HTTP session. + *

+ *

+ * The session context is destroyed: + *

+ * + *
    + *
  • when the HTTPSession times out, after all HttpSessionListeners have been called, or
  • + *
  • at the very end of + * any request in which invalidate() was called, after all filters and ServletRequestListeners have been + * called.
  • + *
+ * + *

+ * An event with qualifier @Initialized(SessionScoped.class) is fired when the session context is initialized and an + * event + * with qualifier @Destroyed(SessionScoped.class) when the session context is destroyed. The event payload is + * the HttpSession + * + * @author Gavin King + * @author Pete Muir + * @author Antoine Sabot-Durand + */ + +@Target({ TYPE, METHOD, FIELD }) +@Retention(RUNTIME) +@Documented +@NormalScope(passivating = true) +@Inherited +public @interface SessionScoped { + + /** + * Supports inline instantiation of the {@link SessionScoped} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ + public final static class Literal extends AnnotationLiteral implements SessionScoped { + + public static final Literal INSTANCE = new Literal(); + + private static final long serialVersionUID = 1L; + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/control/ActivateRequestContext.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/control/ActivateRequestContext.java new file mode 100644 index 000000000..8e12dd65f --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/control/ActivateRequestContext.java @@ -0,0 +1,45 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context.control; + +import javax.interceptor.InterceptorBinding; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * The container provides a built in interceptor that may be used to annotate classes and methods to indicate + * that a request context should be activated when this method is invoked. + * + * The request context will be activated before the method is called, and deactivated when the method invocation is + * complete (regardless of any exceptions being thrown). If the context is already active, it is ignored, neither + * activated nor deactivated. + * + * @since 2.0 + * @author John D. Ament + */ +@InterceptorBinding +@Target({METHOD, TYPE}) +@Retention(RUNTIME) +@Documented +public @interface ActivateRequestContext { +} \ No newline at end of file diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/control/RequestContextController.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/control/RequestContextController.java new file mode 100644 index 000000000..4d09bccd9 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/control/RequestContextController.java @@ -0,0 +1,66 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context.control; + +import javax.enterprise.context.ContextNotActiveException; + +/** + * The CDI container provides a built in instance of RequestContextController that is dependent scoped for the purposes + * of activating and deactivating. For example: + * + *

+ *    @Inject
+ *    private RequestContextController requestContextController;
+ *
+ *    public void doRequest(String body) {
+ *       // activate request context
+ *       requestContextController.activate();
+ *
+ *       // do work in a request context.
+ *
+ *       // deactivate the request context
+ *       requestContextController.deactivate();
+ *    }
+ * 
+ * + * Once the request context has been deactivated, you may activate it once again, creating a brand new request context. + * The activated request context is bound to the current thread, any injection points targeting a request scoped bean + * will be satisfied with the same request scoped objects. + * + * @since 2.0 + * @author John D. Ament + */ +public interface RequestContextController { + + /** + * Activates a RequestContext for the current thread if one is not already active. + * @return true if the context was activated by this invocation, false if not. + */ + boolean activate(); + + /** + * Deactivates the current Request Context if it was activated by this context controller. If the context is active + * but was not activated by this controller, then it may not be deactivated by this controller, + * meaning this method will do nothing. + * + * If the context is not active, a {@see ContextNotActiveException} is thrown. + * + * @throws ContextNotActiveException if the context is not active + */ + void deactivate() throws ContextNotActiveException; +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/package-info.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/package-info.java new file mode 100644 index 000000000..b79adc15f --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/package-info.java @@ -0,0 +1,130 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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. + */ +/** + *

Annotations and interfaces relating to scopes and contexts.

+ * + *

A scope type is a Java annotation annotated + * {@link javax.inject.Scope @Scope} or + * {@link javax.enterprise.context.NormalScope @NormalScope}. + * The scope of a bean determines the lifecycle and visibility of + * its instances. In particular, the scope determines:

+ * + *
    + *
  • When a new instance of the bean is created
  • + *
  • When an existing instance of the bean is destroyed
  • + *
  • Which injected references refer to any instance of the + * bean
  • + *
+ * + *

Built-in scopes

+ * + *

The following built-in scopes are provided: + * {@link javax.enterprise.context.Dependent @Dependent}, + * {@link javax.enterprise.context.RequestScoped @RequestScoped}, + * {@link javax.enterprise.context.ConversationScoped @ConversationScoped}, + * {@link javax.enterprise.context.SessionScoped @SessionScoped}, + * {@link javax.enterprise.context.ApplicationScoped @ApplicationScoped}, + * {@link javax.inject.Singleton @Singleton}.

+ * + *

The container provides an implementation of the Context + * interface for each of the built-in scopes. The built-in request, + * session, and application contexts support servlet, web service + * and EJB invocations. The built-in conversation context supports + * JSF requests.

+ * + *

For other kinds of invocations, a portable extension may define a + * custom {@linkplain javax.enterprise.context.spi.Context context object} + * for any or all of the built-in scopes. For example, a third-party web + * application framework might provide a conversation context object for + * the built-in conversation scope.

+ * + *

The context associated with a built-in scope propagates across + * local, synchronous Java method calls, including invocation of EJB + * local business methods. The context does not propagate across remote + * method invocations or to asynchronous processes such as JMS message + * listeners or EJB timer service timeouts.

+ * + *

Normal scopes and pseudo-scopes

+ * + *

Most scopes are normal scopes. Normal scopes are declared + * using {@link javax.enterprise.context.NormalScope @NormalScope}. + * If a bean has a normal scope, every client executing in a certain + * thread sees the same contextual instance of the bean. This instance is + * called the current instance of the bean. The operation + * {@link javax.enterprise.context.spi.Context#get(javax.enterprise.context.spi.Contextual)} of the + * context object for a normal scope type always returns the current + * instance of the given bean.

+ * + *

Any scope that is not a normal scope is called a pseudo-scope. + * Pseudo-scopes are declared using {@link javax.inject.Scope @Scope}. + * The concept of a current instance is not well-defined in the case of + * a pseudo-scope. Different clients executing in the same thread may + * see different instances of the bean. In the extreme case of the + * {@link javax.enterprise.context.Dependent @Dependent} pseudo-scope, + * every client has its own private instance of the bean.

+ * + *

All built-in scopes are normal scopes, except for the + * {@link javax.enterprise.context.Dependent @Dependent} and + * {@link javax.inject.Singleton @Singleton} pseudo-scopes.

+ * + *

Contextual and injected reference validity

+ * + *

A reference to a bean obtained from the container via {@linkplain + * javax.enterprise.inject.Instance programmatic lookup} is called a + * contextual reference. A contextual reference for a bean with a normal + * scope refers to the current instance of the bean. A contextual + * reference for a bean are valid only for a certain period of time. The + * application should not invoke a method of an invalid reference.

+ * + *

The validity of a contextual reference for a bean depends upon + * whether the scope of the bean is a normal scope or a pseudo-scope:

+ * + *
    + *
  • Any reference to a bean with a normal scope is valid as long as + * the application maintains a hard reference to it. However, it may + * only be invoked when the context associated with the normal scope is + * active. If it is invoked when the context is inactive, a + * {@link javax.enterprise.context.ContextNotActiveException} is thrown + * by the container.
  • + *
  • Any reference to a bean with a pseudo-scope is valid until the + * bean instance to which it refers is destroyed. It may be invoked + * even if the context associated with the pseudo-scope is not active. + * If the application invokes a method of a reference to an instance + * that has already been destroyed, the behavior is undefined.
  • + *
+ * + *

A reference to a bean obtained from the container via {@linkplain + * javax.inject.Inject dependency injection} is a special kind of + * contextual reference, called an injected reference. Additional + * restrictions apply to the validity of an injected reference:

+ * + *
    + *
  • A reference to a bean injected into a field, bean constructor or + * initializer method is only valid until the object into which it was + * injected is destroyed.
  • + *
  • A reference to a bean injected into a producer method is only + * valid until the producer method bean instance that is being produced + * is destroyed.
  • + *
  • A reference to a bean injected into a disposer method or observer + * method is only valid until the invocation of the method completes.
  • + *
+ * + * @see javax.enterprise.inject + * + */ +package javax.enterprise.context; + diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/AlterableContext.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/AlterableContext.java new file mode 100644 index 000000000..3e206aea2 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/AlterableContext.java @@ -0,0 +1,63 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context.spi; + +import javax.enterprise.context.ContextNotActiveException; + +/** + *

+ * Provides an operation for obtaining and destroying contextual instances with a particular scope of any contextual type. Any + * instance of {@code Context} is called a context object. + *

+ * + *

+ * {@link AlterableContext} was introduced in CDI 1.1 to allow bean instances to be destroyed by the application. Extensions + * should implement {@link AlterableContext} instead of {@link Context}. + *

+ * + *

+ * The context object is responsible for creating and destroying contextual instances by calling operations of + * {@link javax.enterprise.context.spi.Contextual}. In particular, the context object is responsible for destroying any + * contextual instance it creates by passing the instance to + * {@link javax.enterprise.context.spi.Contextual#destroy(Object, CreationalContext)} . A destroyed instance must not + * subsequently be returned by {@code get()}. The context object must pass the same instance of + * {@link javax.enterprise.context.spi.CreationalContext} to {@code Contextual.destroy()} that it passed to + * {@code Contextual.create()} when it created the instance. + *

+ * + *

+ * A custom context object may be registered with the container using + * {@link javax.enterprise.inject.spi.AfterBeanDiscovery#addContext(Context)}. + *

+ * + * @author Pete Muir + * @since 1.1 + */ +public interface AlterableContext extends Context { + + /** + *

+ * Destroy the existing contextual instance. If there is no existing instance, no action is taken. + *

+ * + * @param contextual the contextual type + * @throws ContextNotActiveException if the context is not active + */ + public void destroy(Contextual contextual); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/Context.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/Context.java new file mode 100644 index 000000000..ed78e9db9 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/Context.java @@ -0,0 +1,94 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context.spi; + +import java.lang.annotation.Annotation; + +import javax.enterprise.context.ContextNotActiveException; + +/** + *

+ * Provides an operation for obtaining contextual instances with a particular scope of any contextual type. Any instance of + * {@code Context} is called a context object. + *

+ * + *

+ * {@link AlterableContext} was introduced in CDI 1.1 to allow bean instances to be destroyed by the application. Extensions + * should implement {@link AlterableContext} instead of {@link Context}. + *

+ * + *

+ * The context object is responsible for creating and destroying contextual instances by calling operations of + * {@link javax.enterprise.context.spi.Contextual}. In particular, the context object is responsible for destroying any + * contextual instance it creates by passing the instance to + * {@link javax.enterprise.context.spi.Contextual#destroy(Object, CreationalContext)} . A destroyed instance must not + * subsequently be returned by {@code get()}. The context object must pass the same instance of + * {@link javax.enterprise.context.spi.CreationalContext} to {@code Contextual.destroy()} that it passed to + * {@code Contextual.create()} when it created the instance. + *

+ * + *

+ * A custom context object may be registered with the container using + * {@link javax.enterprise.inject.spi.AfterBeanDiscovery#addContext(Context)}. + *

+ * + * @author Gavin King + * @author Pete Muir + */ + +public interface Context { + + /** + * Get the scope type of the context object. + * + * @return the scope + */ + public Class getScope(); + + /** + * Return an existing instance of certain contextual type or create a new instance by calling + * {@link javax.enterprise.context.spi.Contextual#create(CreationalContext)} and return the new instance. + * + * @param the type of contextual type + * @param contextual the contextual type + * @param creationalContext the context in which the new instance will be created + * @return the contextual instance + * + * @throws ContextNotActiveException if the context is not active + */ + public T get(Contextual contextual, CreationalContext creationalContext); + + /** + * Return an existing instance of a certain contextual type or a null value. + * + * @param the type of the contextual type + * @param contextual the contextual type + * @return the contextual instance, or a null value + * + * @throws ContextNotActiveException if the context is not active + */ + public T get(Contextual contextual); + + /** + * Determines if the context object is active. + * + * @return true if the context is active, or false otherwise. + */ + public boolean isActive(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/Contextual.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/Contextual.java new file mode 100644 index 000000000..7f44215bd --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/Contextual.java @@ -0,0 +1,58 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context.spi; + +import javax.enterprise.inject.CreationException; + +/** + *

+ * Defines operations to create and destroy contextual instances of a certain type. Any implementation of {@code Contextual} is + * called a contextual type. In particular, all beans are contextual types. + *

+ * + * @see javax.enterprise.inject.spi.Bean + * + * @author Gavin King + * @author Nicklas Karlsson + * @author Pete Muir + * @param type of the instance + */ +public interface Contextual { + /** + * Create a new instance of the contextual type. Instances should use the given + * {@link javax.enterprise.context.spi.CreationalContext} when obtaining contextual references to inject, in order to ensure + * that any dependent objects are associated with the contextual instance that is being created. An implementation may call + * {@link javax.enterprise.context.spi.CreationalContext#push(Object)} between instantiation and injection to help the + * container minimize the use of client proxy objects. + * + * @param creationalContext the context in which this instance is being created + * @return the contextual instance + * @throws CreationException if a checked exception occurs while creating the instance + */ + public T create(CreationalContext creationalContext); + + /** + * Destroy an instance of the contextual type. Implementations should call + * {@link javax.enterprise.context.spi.CreationalContext#release()} to allow the container to destroy dependent objects of + * the contextual instance. + * + * @param instance the contextual instance to destroy + * @param creationalContext the context in which this instance was created + */ + public void destroy(T instance, CreationalContext creationalContext); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/CreationalContext.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/CreationalContext.java new file mode 100644 index 000000000..eda4fac18 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/CreationalContext.java @@ -0,0 +1,47 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.context.spi; + +/** + *

+ * Provides operations that are used by the {@link javax.enterprise.context.spi.Contextual} implementation during instance + * creation and destruction. + *

+ * + * @author Gavin King + * @author Pete Muir + * + * @param type of the instances on which this CreationalContext operates + */ +public interface CreationalContext { + + /** + * Registers an incompletely initialized contextual instance the with the container. A contextual instance is considered + * incompletely initialized until it is returned by + * {@link javax.enterprise.context.spi.Contextual#create(CreationalContext)} . + * + * @param incompleteInstance the incompletely initialized instance + */ + public void push(T incompleteInstance); + + /** + * Destroys all dependent objects of the instance which is being destroyed, by passing each dependent object to + * {@link javax.enterprise.context.spi.Contextual#destroy(Object, CreationalContext)} . + */ + public void release(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/package-info.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/package-info.java new file mode 100644 index 000000000..225db58b7 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/context/spi/package-info.java @@ -0,0 +1,34 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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. + */ +/** + *

The custom context SPI.

+ * + *

Associated with every + * {@linkplain javax.enterprise.context scope type} is a + * {@linkplain javax.enterprise.context.spi.Context context object}. + * The context object implements the semantics of the scope type.

+ * + *

The context implementation collaborates with the container via + * the {@link javax.enterprise.context.spi.Context Context} and + * {@link javax.enterprise.context.spi.Contextual Contextual} + * interfaces to create and destroy contextual instances.

+ * + * @see javax.enterprise.context + * @see javax.enterprise.inject.spi + */ +package javax.enterprise.context.spi; + diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/Event.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/Event.java new file mode 100644 index 000000000..e2138e7bb --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/Event.java @@ -0,0 +1,186 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, 2015, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.event; + +import java.lang.annotation.Annotation; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Executor; + +import javax.enterprise.util.TypeLiteral; + +/** + *

+ * Allows the application to fire events of a particular type. + *

+ * + *

+ * Beans fire events via an instance of the Event interface, which may be injected: + *

+ * + *
+ * @Inject
+ * @Any
+ * Event<LoggedInEvent> loggedInEvent;
+ * 
+ * + *

+ * The fire() method accepts an event object: + *

+ * + *
+ * public void login() { 
+ *    ...
+ *    loggedInEvent.fire( new LoggedInEvent(user) );
+ * }
+ * 
+ * + *

+ * Any combination of qualifiers may be specified at the injection point: + *

+ * + *
+ * @Inject
+ * @Admin
+ * Event<LoggedInEvent> adminLoggedInEvent;
+ * 
+ * + *

+ * Or, the {@link javax.enterprise.inject.Any @Any} qualifier may be used, allowing the application to specify qualifiers + * dynamically: + *

+ * + *
+ * @Inject
+ * @Any
+ * Event<LoggedInEvent> loggedInEvent;
+ * 
+ * + *

+ * For an injected Event: + *

+ * + *
    + *
  • the specified type is the type parameter specified at the injection point, and
  • + *
  • the specified qualifiers are the qualifiers specified at the injection point.
  • + *
+ * + *

+ * Events may also be fired asynchronously with {@link #fireAsync(Object)} and {@link #fireAsync(Object, NotificationOptions)} methods + *

+ * + * @author Gavin King + * @author Pete Muir + * @author David Allen + * @author Antoine Sabot-Durand + * + * @param the type of the event object + */ + +public interface Event { + + /** + *

+ * Fires an event with the specified qualifiers and notifies observers. + *

+ * + * @param event the event object + * @throws IllegalArgumentException if the runtime type of the event object contains a type variable + * @throws ObserverException if a notified observer throws a checked exception, it will be wrapped and rethrown as an + * (unchecked) {@link ObserverException} + */ + public void fire(T event); + + /** + *

+ * Fires an event asynchronously with the specified qualifiers and notifies asynchronous observers. + *

+ * + * @param event the event object + * @return a {@link CompletionStage} allowing further pipeline composition on the asynchronous operation. + * Default asynchronous execution facility is container specific. + * If any observer notified by this event throws an exception + * then the resulting CompletionStage is completed exceptionally with {@link java.util.concurrent.CompletionException} + * that wraps all the exceptions raised by observers as suppressed exception. + * If no exception is thrown by observers then the resulting CompletionStage is completed normally with the event payload. + * @throws IllegalArgumentException if the runtime type of the event object contains a type variable + * + * @since 2.0 + */ + public CompletionStage fireAsync(U event); + + /** + *

+ * Fires an event asynchronously with the specified qualifiers and notifies asynchronous observers. + * A custom {@link Executor} will be used to make asynchronous calls + *

+ * + * @param event the event object + * @param options the notification options + * @return a {@link CompletionStage} allowing further pipeline composition on the asynchronous operation. + * Default asynchronous execution facility is container specific. + * If any observer notified by this event throws an exception + * then the resulting CompletionStage is completed exceptionally with {@link java.util.concurrent.CompletionException} + * that wraps all the exceptions raised by observers as suppressed exception. + * If no exception is thrown by observers then the resulting CompletionStage is completed normally with the event payload. + * @throws IllegalArgumentException if the runtime type of the event object contains a type variable + * + * @since 2.0 + */ + public CompletionStage fireAsync(U event, NotificationOptions options); + + /** + *

+ * Obtains a child Event for the given additional required qualifiers. + *

+ * + * @param qualifiers the additional specified qualifiers + * @return the child Event + * @throws IllegalArgumentException if passed two instances of the same non repeating qualifier type, or an instance of an annotation that + * is not a qualifier type + */ + public Event select(Annotation... qualifiers); + + /** + *

+ * Obtains a child Event for the given required type and additional required qualifiers. + *

+ * + * @param the specified type + * @param subtype a {@link java.lang.Class} representing the specified type + * @param qualifiers the additional specified qualifiers + * @return the child Event + * @throws IllegalArgumentException if passed two instances of the same non repeating qualifier type, or an instance of an annotation that + * is not a qualifier type + */ + public Event select(Class subtype, Annotation... qualifiers); + + /** + *

+ * Obtains a child Event for the given required type and additional required qualifiers. + *

+ * + * @param the specified type + * @param subtype a {@link javax.enterprise.util.TypeLiteral} representing the specified type + * @param qualifiers the additional specified qualifiers + * @return the child Event + * @throws IllegalArgumentException if passed two instances of the same non repeating qualifier type, or an instance of an annotation that + * is not a qualifier type + */ + public Event select(TypeLiteral subtype, Annotation... qualifiers); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/ImmutableNotificationOptions.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/ImmutableNotificationOptions.java new file mode 100644 index 000000000..784b095e8 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/ImmutableNotificationOptions.java @@ -0,0 +1,76 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.event; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +/** + * The immutable implementation of {@link NotificationOptions}. + * + * @author Martin Kouba + * + */ +class ImmutableNotificationOptions implements NotificationOptions { + + private final Executor executor; + + private final Map options; + + private ImmutableNotificationOptions(Executor executor, Map options) { + this.executor = executor; + this.options = new HashMap<>(options); + } + + @Override + public Executor getExecutor() { + return executor; + } + + @Override + public Object get(String name) { + return options.get(name); + } + + static class Builder implements javax.enterprise.event.NotificationOptions.Builder { + + private Executor executor; + + private Map options; + + Builder() { + this.options = new HashMap<>(); + } + + public Builder setExecutor(Executor executor) { + this.executor = executor; + return this; + } + + public Builder set(String name, Object value) { + options.put(name, value); + return this; + } + + public ImmutableNotificationOptions build() { + return new ImmutableNotificationOptions(executor, options); + } + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/NotificationOptions.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/NotificationOptions.java new file mode 100644 index 000000000..823f1e4da --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/NotificationOptions.java @@ -0,0 +1,86 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.event; + +import java.util.concurrent.Executor; + +/** + * Notification options are used to configure observer notification. + * + * @author Martin Kouba + * @see Event#fireAsync(Object, NotificationOptions) + * @since 2.0 + */ +public interface NotificationOptions { + + /** + * + * @return the executor used to execute an asynchronous event + */ + Executor getExecutor(); + + /** + * + * @param optionName name of the option to get value of + * @return the value of an option or null if no option for the given name exists + */ + Object get(String optionName); + + /** + * + * @param executor a specific {@link Executor} to handle observer notification + * @return an immutable holder of an executor + */ + static NotificationOptions ofExecutor(Executor executor) { + return builder().setExecutor(executor).build(); + } + + /** + * + * @param optionName name of the option to set + * @param optionValue value for the option + * @return an immutable holder of a single option + */ + static NotificationOptions of(String optionName, Object optionValue) { + return builder().set(optionName, optionValue).build(); + } + + /** + * + * @return the options builder + */ + static Builder builder() { + return new ImmutableNotificationOptions.Builder(); + } + + /** + * Notification options builder. + * + * @author Martin Kouba + * @since 2.0 + */ + interface Builder { + + Builder setExecutor(Executor executor); + + Builder set(String optionName, Object optionValue); + + NotificationOptions build(); + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/ObserverException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/ObserverException.java new file mode 100644 index 000000000..91e282cb4 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/ObserverException.java @@ -0,0 +1,48 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.event; + +/** + *

+ * Indicates that a checked exception was thrown by an observer method during event notification. + *

+ * + * @author Pete Muir + * @author Gavin King + */ +public class ObserverException extends RuntimeException { + + private static final long serialVersionUID = -801836224808304381L; + + public ObserverException() { + + } + + public ObserverException(String message) { + super(message); + } + + public ObserverException(Throwable cause) { + super(cause); + } + + public ObserverException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/Observes.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/Observes.java new file mode 100644 index 000000000..7a772a5e1 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/Observes.java @@ -0,0 +1,109 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.event; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + *

+ * Identifies the event parameter of an observer method. May be applied to a parameter of a method of a bean class or + * {@linkplain javax.enterprise.inject.spi.Extension extension}. + *

+ * + *
+ * public void afterLogin(@Observes LoggedInEvent event) { ... }
+ * 
+ * + *

+ * An observer method is a non-abstract method of a managed bean class or session bean class (or of an extension). An observer + * method may be either static or non-static. If the bean is a session bean, the observer method must be either a business + * method of the EJB or a static method of the bean class. + *

+ * + *

+ * Each observer method must have exactly one event parameter, of the same type as the event type it observes. Event qualifiers + * may be declared by annotating the event parameter. When searching for observer methods for an event, the container considers + * the type and qualifiers of the event parameter. + *

+ * + *

+ * If the event parameter does not explicitly declare any qualifier, the observer method observes events with no qualifier. + *

+ * + *

+ * The event parameter type may contain a type variable or wildcard. + *

+ * + *

+ * In addition to the event parameter, observer methods may declare additional parameters, which may declare qualifiers. These + * additional parameters are injection points. + *

+ * + *
+ * public void afterLogin(@Observes LoggedInEvent event, @Manager User user, Logger log) { ... }
+ * 
+ * + *

+ * A bean (or extension) may declare multiple observer methods. + *

+ * + *

+ * Observer methods are inherited by bean subclasses. + *

+ * + *

+ * Interceptors and decorators may not declare observer methods. + *

+ * + * @author Gavin King + * @author Pete Muir + * @author David Allen + */ + +@Target(PARAMETER) +@Retention(RUNTIME) +@Documented +public @interface Observes { + /** + *

+ * Specifies {@linkplain javax.enterprise.event.Reception under what conditions the observer method is notified}. + *

+ * + *

+ * By default, the observer method is notified even if no instance of the bean that defines the observer method already + * exists in the current context. + *

+ */ + public Reception notifyObserver() default Reception.ALWAYS; + + /** + *

+ * Specifies {@linkplain javax.enterprise.event.Reception at what time the observer method is notified}. + *

+ * + *

+ * By default, the observer method is notified when the event is fired. + *

+ */ + public TransactionPhase during() default TransactionPhase.IN_PROGRESS; +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/ObservesAsync.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/ObservesAsync.java new file mode 100644 index 000000000..4cbad0434 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/ObservesAsync.java @@ -0,0 +1,99 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2015, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.event; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + *

+ * Identifies the event parameter of an asynchronous observer method. May be applied to a parameter of a method of a bean class + *

+ * + *
+ * public void afterLogin(@ObservesAsync LoggedInEvent event) { ... }
+ * 
+ * + *

+ * An observer method is a non-abstract method of a managed bean class or session bean class (or of an extension). An observer + * method may be either static or non-static. If the bean is a session bean, the observer method must be either a business + * method of the EJB or a static method of the bean class. + *

+ * + *

+ * Each observer method must have exactly one event parameter, of the same type as the event type it observes. Event qualifiers + * may be declared by annotating the event parameter. When searching for observer methods for an event, the container considers + * the type and qualifiers of the event parameter. + *

+ * + *

+ * If the event parameter does not explicitly declare any qualifier, the observer method observes events with no qualifier. + *

+ * + *

+ * The event parameter type may contain a type variable or wildcard. + *

+ * + *

+ * In addition to the event parameter, observer methods may declare additional parameters, which may declare qualifiers. These + * additional parameters are injection points. + *

+ * + *
+ * public void afterLogin(@ObservesAsync LoggedInEvent event, @Manager User user, Logger log) { ... }
+ * 
+ * + *

+ * A bean (or extension) may declare multiple observer methods. + *

+ * + *

+ * Observer methods are inherited by bean subclasses. + *

+ * + *

+ * Interceptors and decorators may not declare observer methods. + *

+ * + * @author Gavin King + * @author Pete Muir + * @author David Allen + */ + +@Target(PARAMETER) +@Retention(RUNTIME) +@Documented +public @interface ObservesAsync { + /** + *

+ * Specifies {@linkplain Reception under what conditions the observer method is notified}. + *

+ * + *

+ * By default, the observer method is notified even if no instance of the bean that defines the observer method already + * exists in the current context. + *

+ */ + public Reception notifyObserver() default Reception.ALWAYS; + + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/Reception.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/Reception.java new file mode 100644 index 000000000..94a11db88 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/Reception.java @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.event; + +/** + *

+ * Distinguishes conditional {@linkplain javax.enterprise.event.Observes observer methods} from observer methods which are + * always notified. + *

+ * + *

+ * A conditional observer method is an observer method which is notified of an event only if an instance of the bean that + * defines the observer method already exists in the current context. + *

+ * + *

+ * Beans with scope {@link javax.enterprise.context.Dependent @Dependent} may not have conditional observer methods. + *

+ * + * @author Gavin King + * @author Dan Allen + * @author David Allen + */ +public enum Reception { + /** + *

+ * Specifies that an observer method is only called if the current instance of the bean declaring the observer method + * already exists. + *

+ *

+ * If there is no active context for the scope to which the bean declaring this observer method belongs, then the observer + * method is not called. + *

+ */ + IF_EXISTS, + + /** + * Specifies that an observer method always receives event notifications. + */ + ALWAYS +} \ No newline at end of file diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/TransactionPhase.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/TransactionPhase.java new file mode 100644 index 000000000..0180f771a --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/TransactionPhase.java @@ -0,0 +1,92 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.event; + +/** + *

+ * Distinguishes the various kinds of transactional {@linkplain javax.enterprise.event.Observes observer methods} from regular + * observer methods which are notified immediately. + *

+ *

+ * Transactional observer methods are observer methods which receive event notifications during the before or after completion + * phase of the transaction in which the event was fired. If no transaction is in progress when the event is fired, they are + * notified at the same time as other observers. + * If the transaction is in progress, but {@link javax.transaction.Synchronization} callback cannot be registered due to the transaction being already + * marked for rollback or in state where {@link javax.transaction.Synchronization} callbacks cannot be registered, the {@link #BEFORE_COMPLETION}, + * {@link #AFTER_COMPLETION} and {@link #AFTER_FAILURE} observer methods are notified at the same time as other observers, + * but {@link #AFTER_SUCCESS} observer methods get skipped. + *

+ * + * @author Pete Muir + * @author Gavin King + * + */ +public enum TransactionPhase { + + /** + *

+ * Identifies a regular observer method, called when the event is fired. + *

+ */ + IN_PROGRESS, + + /** + *

+ * Identifies a before completion observer method, called during the before completion phase of the transaction. + *

+ *

+ * Transactional observer will be notified if there is no transaction in progress, or the transaction is in progress, + * but {@link javax.transaction.Synchronization} callback cannot be registered due to the transaction being already + * marked for rollback or in state where {@link javax.transaction.Synchronization} callbacks cannot be registered. + *

+ */ + BEFORE_COMPLETION, + + /** + *

+ * Identifies an after completion observer method, called during the after completion phase of the transaction. + *

+ *

+ * Transactional observer will be notified if there is no transaction in progress, or the transaction is in progress, + * but {@link javax.transaction.Synchronization} callback cannot be registered due to the transaction being already + * marked for rollback or in state where {@link javax.transaction.Synchronization} callbacks cannot be registered. + *

+ */ + AFTER_COMPLETION, + + /** + *

+ * Identifies an after failure observer method, called during the after completion phase of the transaction, only when the + * transaction fails. + *

+ *

+ * Transactional observer will be notified will also get invoked if there is no transaction in progress, or the transaction is in progress, + * but {@link javax.transaction.Synchronization} callback cannot be registered due to the transaction being already + * marked for rollback or in state where {@link javax.transaction.Synchronization} callbacks cannot be registered. + *

+ */ + AFTER_FAILURE, + + /** + *

+ * Identifies an after success observer method, called during the after completion phase of the transaction, only when the + * transaction completes successfully. + *

+ */ + AFTER_SUCCESS + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/package-info.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/package-info.java new file mode 100644 index 000000000..b25a757aa --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/event/package-info.java @@ -0,0 +1,118 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, 2015, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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. + */ +/** + *

Annotations and interfaces relating to events.

+ * + *

{@linkplain javax.enterprise.inject Beans} may produce and + * consume events. Events allows beans to interact in a completely + * decoupled fashion, with no compile-time dependency between the + * interacting beans. Most importantly, it allows stateful beans + * in one architectural tier of the application to synchronize + * their internal state with state changes that occur in a + * different tier.

+ * + *

Events may be fired synchronously or asynchronously.

+ * + *

An event comprises:

+ * + *
    + *
  • A Java object, called the event object
  • + *
  • A (possibly empty) set of instances of qualifier types, called + * the event qualifiers
  • + *
+ * + *

The {@link javax.enterprise.event.Event} interface is used to + * fire events.

+ * + *

Event objects and event types

+ * + *

The event object acts as a payload, to propagate state from + * producer to consumer. An event object is an instance of a concrete + * Java class with no type variables.

+ * + *

The event types of the event include all superclasses and + * interfaces of the runtime class of the event object. An event type + * may not contain a type variable.

+ * + *

Event qualifiers

+ * + *

The event qualifiers act as topic selectors, allowing the consumer + * to narrow the set of events it observes. An event qualifier may be an + * instance of any {@linkplain javax.inject.Qualifier qualifier type}.

+ * + *

Observer methods

+ * + *

An {@linkplain javax.enterprise.event.Observes observer method} + * allows the application to receive and respond synchronously to event notifications. + * And an {@linkplain javax.enterprise.event.ObservesAsync async observer method} + * allows the application to receive and respond asynchronously to event notifications. + * they both act as event consumers, observing events of a specific type, with a + * specific set of qualifiers. Any Java type may be observed by an + * observer method.

+ * + *

An observer method is a method of a bean class or + * {@linkplain javax.enterprise.inject.spi.Extension extension} with a + * parameter annotated {@link javax.enterprise.event.Observes @Observes} + * or {@link javax.enterprise.event.ObservesAsync @ObservesAsync}.

+ * + *

An observer method will be notified of an event if:

+ * + *
    + *
  • the event object is assignable to the type observed by the observer + * method,
  • + *
  • the observer method has all the event qualifiers of the event, and
  • + *
  • either the event is not a + * {@linkplain javax.enterprise.inject.spi container lifecycle event}, or + * the observer method belongs to an + * {@linkplain javax.enterprise.inject.spi.Extension extension}. + *
+ * + *

If a synchronous observer method is a + * {@linkplain javax.enterprise.event.TransactionPhase transactional + * observer method} and there is a JTA transaction in progress when the + * event is fired, the observer method is notified during the appropriate + * transaction completion phase. Otherwise, the observer is notified when + * the event is fired.

+ * + *

The order in which observer methods are called depends on the value of + * the {@linkplain javax.annotation.Priority @Priority} applied to the observer.

+ *

If no priority is defined on a observer, its priority is javax.interceptor.Interceptor.Priority.APPLICATION+500.

+ *

If two observer have the same priority their relative order is undefined.

+ * + *

Observer methods may throw exceptions:

+ * + *
    + *
  • If the observer method is a + * {@linkplain javax.enterprise.event.TransactionPhase transactional + * observer method}, any exception is caught and logged by the container.
  • + *
  • If the observer method is asynchronous, any exception is caught by the container and added as a suppressed exception + * to a {@link java.util.concurrent.CompletionException} that could be handle by the application
  • + *
  • Otherwise, the exception aborts processing of the event. + * No other observer methods of that event will be called. The + * exception is rethrown. If the exception is a checked exception, + * it is wrapped and rethrown as an (unchecked) + * {@link javax.enterprise.event.ObserverException}.
  • + *
+ * + * @see javax.enterprise.inject + * + * @see javax.enterprise.event.Observes + * @see javax.enterprise.event.Event + * @see javax.inject.Qualifier + */ +package javax.enterprise.event; + diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Alternative.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Alternative.java new file mode 100644 index 000000000..13768b814 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Alternative.java @@ -0,0 +1,83 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.util.AnnotationLiteral; + +/** + *

+ * Specifies that a bean is an alternative. May be applied to a bean class, producer method or field or + * {@linkplain javax.enterprise.inject.Stereotype stereotype}. + *

+ * + *
+ * @Alternative
+ * public class MockOrder extends Order { ... }
+ * 
+ * + *

+ * An alternative is not available for injection, lookup or EL resolution to classes or JSP/JSF pages in a module unless the + * module is a bean archive and the alternative is explicitly selected in that bean archive. An alternative is never + * available for injection, lookup or EL resolution in a module that is not a bean archive. + *

+ * + *

+ * By default, a bean archive has no selected alternatives. An alternative must be explicitly declared using the + * <alternatives> element of the beans.xml file of the bean archive. The <alternatives> + * element contains a list of bean classes and stereotypes. An alternative is selected for the bean archive if either: + *

+ * + *
    + *
  • the alternative is a managed bean or session bean and the bean class of the bean is listed,
  • + *
  • the alternative is a producer method, field or resource, and the bean class that declares the method or field is listed, + * or
  • + *
  • any @Alternative stereotype of the alternative is listed.
  • + *
+ * + * @author Gavin King + * @author Pete Muir + */ +@Target({ TYPE, METHOD, FIELD }) +@Retention(RUNTIME) +@Documented +public @interface Alternative { + + /** + * Supports inline instantiation of the {@link Alternative} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ + public final static class Literal extends AnnotationLiteral implements Alternative { + + public static final Literal INSTANCE = new Literal(); + + private static final long serialVersionUID = 1L; + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/AmbiguousResolutionException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/AmbiguousResolutionException.java new file mode 100644 index 000000000..69f4f15f3 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/AmbiguousResolutionException.java @@ -0,0 +1,47 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +/** + *

+ * Indicates that multiple beans match a certain combination of required type and required qualifiers and are eligible for + * injection into a certain class. + *

+ * + * @author Pete Muir + * @author Gavin King + */ +public class AmbiguousResolutionException extends ResolutionException { + + private static final long serialVersionUID = -2132733164534544788L; + + public AmbiguousResolutionException() { + } + + public AmbiguousResolutionException(String message, Throwable throwable) { + super(message, throwable); + } + + public AmbiguousResolutionException(String message) { + super(message); + } + + public AmbiguousResolutionException(Throwable throwable) { + super(throwable); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Any.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Any.java new file mode 100644 index 000000000..bf0c9c45d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Any.java @@ -0,0 +1,97 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.event.Event; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Qualifier; + +/** + *

+ * The built-in qualifier type. + *

+ * + *

+ * Every bean has the qualifier @Any, even if it does not explicitly declare this qualifier, except for the + * special {@link javax.enterprise.inject.New @New qualified beans}. + *

+ * + *

+ * Every event has the qualifier @Any, even if it was raised without explicitly declaration of this qualifier. + *

+ * + *

+ * The @Any qualifier allows an injection point to refer to all beans or all events of a certain bean type. + *

+ * + *
+ * @Inject
+ * @Any
+ * Instance<PaymentProcessor> anyPaymentProcessor;
+ * 
+ * + *
+ * @Inject
+ * @Any
+ * Event<User> anyUserEvent;
+ * 
+ * + *
+ * @Inject
+ * @Delegate
+ * @Any
+ * Logger logger;
+ * 
+ * + * @author Gavin King + * @author David Allen + */ + +@Qualifier +@Retention(RUNTIME) +@Target({ TYPE, METHOD, FIELD, PARAMETER }) +@Documented +public @interface Any { + + /** + * Supports inline instantiation of the {@link Any} qualifier. + * + * @author Martin Kouba + * @since 2.0 + * @see Instance + * @see Event + */ + public static final class Literal extends AnnotationLiteral implements Any { + + public static final Literal INSTANCE = new Literal(); + + private static final long serialVersionUID = 1L; + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/CreationException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/CreationException.java new file mode 100644 index 000000000..a430b0da3 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/CreationException.java @@ -0,0 +1,48 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +/** + *

+ * Indicates that a checked exception was thrown during creation of a bean. + *

+ * + * @author Pete Muir + * @author Gavin King + */ +public class CreationException extends InjectionException { + + private static final long serialVersionUID = 1002854668862145298L; + + public CreationException() { + + } + + public CreationException(String message) { + super(message); + } + + public CreationException(Throwable cause) { + super(cause); + } + + public CreationException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Decorated.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Decorated.java new file mode 100644 index 000000000..e68d7485e --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Decorated.java @@ -0,0 +1,62 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + *

+ * A decorator may inject metadata about the bean it is decorating + *

+ * + *
+ * @Decorator
+ * class TimestampLogger implements Logger {
+ *     @Inject
+ *     @Delegate
+ *     @Any
+ *     Logger logger;
+ * 
+ *     @Inject
+ *     @Decorated
+ *     Bean<Logger> bean;
+ * 
+ *     void log(String message) {
+ *       ...
+ *    }
+ * }
+ * 
+ * + * @author Pete Muir + * @since 1.1 + */ + +@Target({ PARAMETER, FIELD }) +@Retention(RUNTIME) +@Documented +@Qualifier +public @interface Decorated { +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Default.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Default.java new file mode 100644 index 000000000..11c13fbdc --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Default.java @@ -0,0 +1,111 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.event.Event; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Qualifier; + +/** + *

+ * The default qualifier type. + *

+ * + *

+ * If a bean does not explicitly declare a qualifier other than {@link javax.inject.Named @Named}, the bean has the + * qualifier @Default. + *

+ * + *

+ * If an injection point declares no qualifier, the injection point has exactly one qualifier, the default qualifier + * @Default. + *

+ * + *

+ * The following are equivalent: + *

+ * + *
+ * @ConversationScoped
+ * public class Order {
+ *
+ *     private Product product;
+ *     private User customer;
+ *
+ *     @Inject
+ *     public void init(@Selected Product product, User customer) {
+ *         this.product = product;
+ *         this.customer = customer;
+ *     }
+ *
+ * }
+ * 
+ * + *
+ * @Default
+ * @ConversationScoped
+ * public class Order {
+ *
+ *     private Product product;
+ *     private User customer;
+ *
+ *     @Inject
+ *     public void init(@Selected Product product, @Default User customer) {
+ *         this.product = product;
+ *         this.customer = customer;
+ *     }
+ *
+ * }
+ * 
+ * + * @author Pete Muir + * @author Gavin King + */ + +@Target({ TYPE, METHOD, PARAMETER, FIELD }) +@Retention(RUNTIME) +@Documented +@Qualifier +public @interface Default { + + /** + * Supports inline instantiation of the {@link Default} qualifier. + * + * @author Martin Kouba + * @since 2.0 + * @see Instance + * @see Event + */ + public static final class Literal extends AnnotationLiteral implements Default { + + public static final Literal INSTANCE = new Literal(); + + private static final long serialVersionUID = 1L; + + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Disposes.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Disposes.java new file mode 100644 index 000000000..0b5888129 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Disposes.java @@ -0,0 +1,120 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + *

+ * Identifies the disposed parameter of a disposer method. May be applied to a parameter of a method of a bean class. + *

+ * + *
+ * public class UserDatabaseEntityManager {
+ * 
+ *     @Produces
+ *     @ConversationScoped
+ *     @UserDatabase
+ *     public EntityManager create(EntityManagerFactory emf) {
+ *         return emf.createEntityManager();
+ *     }
+ * 
+ *     public void close(@Disposes @UserDatabase EntityManager em) {
+ *         em.close();
+ *     }
+ * 
+ * }
+ * 
+ * + *
+ * public class Resources {
+ * 
+ *     @PersistenceContext
+ *     @Produces
+ *     @UserDatabase
+ *     private EntityManager em;
+ * 
+ *     public void close(@Disposes @UserDatabase EntityManager em) {
+ *         em.close();
+ *     }
+ * 
+ * }
+ * 
+ * + *

+ * A disposer method allows the application to perform customized cleanup of an object returned by a + * {@linkplain javax.enterprise.inject.Produces producer method or producer field}. + *

+ * + *

+ * A disposer method must be a non-abstract method of a managed bean class or session bean class. A disposer method may be + * either static or non-static. If the bean is a session bean, the disposer method must be a business method of the EJB or a + * static method of the bean class. + *

+ * + *

+ * A bean may declare multiple disposer methods. + *

+ * + *

+ * Each disposer method must have exactly one disposed parameter, of the same type as the corresponding producer method or + * producer field return type. When searching for disposer methods for a producer method or producer field, the container + * considers the type and qualifiers of the disposed parameter. If a disposed parameter resolves to a producer method or + * producer field declared by the same bean class, the container must call this method when destroying any instance returned by + * that producer method or producer field. + *

+ * + *

+ * In addition to the disposed parameter, a disposer method may declare additional parameters, which may also specify + * qualifiers. These additional parameters are injection points. + *

+ * + *
+ * public void close(@Disposes @UserDatabase EntityManager em, Logger log) { ... }
+ * 
+ * + *

+ * A disposer method may resolve to multiple producer methods or producer fields declared by the bean class, in which case the + * container must call it when destroying any instance returned by any of these producer methods or producer fields. + *

+ * + *

+ * Disposer methods are not inherited by bean subclasses. + *

+ * + *

+ * Interceptors and decorators may not declare disposer methods. + *

+ * + * @see javax.enterprise.inject.Produces @Produces + * + * @author Gavin King + * @author Pete Muir + */ + +@Target(PARAMETER) +@Retention(RUNTIME) +@Documented +public @interface Disposes { + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/IllegalProductException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/IllegalProductException.java new file mode 100644 index 000000000..64881597c --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/IllegalProductException.java @@ -0,0 +1,45 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +/** + *

+ * Indicates that a producer method returned a null value or a producer field contained a null value, and the scope of the + * producer method or field was not {@link javax.enterprise.context.Dependent}. + *

+ */ +public class IllegalProductException extends InjectionException { + + private static final long serialVersionUID = -6280627846071966243L; + + public IllegalProductException() { + super(); + } + + public IllegalProductException(String message, Throwable cause) { + super(message, cause); + } + + public IllegalProductException(String message) { + super(message); + } + + public IllegalProductException(Throwable cause) { + super(cause); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/InjectionException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/InjectionException.java new file mode 100644 index 000000000..5f615c319 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/InjectionException.java @@ -0,0 +1,44 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +/** + * Indicates a problem relating to dependency injection. + * + * @author Pete Muir + */ +public class InjectionException extends RuntimeException { + + private static final long serialVersionUID = -2132733164534544788L; + + public InjectionException() { + } + + public InjectionException(String message, Throwable throwable) { + super(message, throwable); + } + + public InjectionException(String message) { + super(message); + } + + public InjectionException(Throwable throwable) { + super(throwable); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Instance.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Instance.java new file mode 100644 index 000000000..cde42db5f --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Instance.java @@ -0,0 +1,240 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import java.lang.annotation.Annotation; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import javax.enterprise.util.TypeLiteral; +import javax.inject.Provider; + +/** + *

+ * Allows the application to dynamically obtain instances of beans with a specified combination of required type and qualifiers. + *

+ * + *

+ * In certain situations, injection is not the most convenient way to obtain a contextual reference. For example, it may not be + * used when: + *

+ * + *
    + *
  • the bean type or qualifiers vary dynamically at runtime, or
  • + *
  • depending upon the deployment, there may be no bean which satisfies the type and qualifiers, or
  • + *
  • we would like to iterate over all beans of a certain type.
  • + *
+ * + *

+ * In these situations, an instance of the Instance may be injected: + *

+ * + *
+ * @Inject
+ * Instance<PaymentProcessor> paymentProcessor;
+ * 
+ * + *

+ * Any combination of qualifiers may be specified at the injection point: + *

+ * + *
+ * @Inject
+ * @PayBy(CHEQUE)
+ * Instance<PaymentProcessor> chequePaymentProcessor;
+ * 
+ * + *

+ * Or, the {@link javax.enterprise.inject.Any @Any} qualifier may be used, allowing the application to specify qualifiers + * dynamically: + *

+ * + *
+ * @Inject
+ * @Any
+ * Instance<PaymentProcessor> anyPaymentProcessor;
+ * 
+ * + *

+ * Finally, the {@link javax.enterprise.inject.New @New} qualifier may be used, allowing the application to obtain a + * {@link javax.enterprise.inject.New @New} qualified bean: + *

+ * + *
+ * @Inject
+ * @New(ChequePaymentProcessor.class)
+ * Instance<PaymentProcessor> chequePaymentProcessor;
+ * 
+ * + *

+ * For an injected Instance: + *

+ * + *
    + *
  • the required type is the type parameter specified at the injection point, and
  • + *
  • the required qualifiers are the qualifiers specified at the injection point.
  • + *
+ * + *

+ * The inherited {@link javax.inject.Provider#get()} method returns a contextual references for the unique bean that matches the + * required type and required qualifiers and is eligible for injection into the class into which the parent Instance + * was injected, or throws an {@link javax.enterprise.inject.UnsatisfiedResolutionException} or + * {@link javax.enterprise.inject.AmbiguousResolutionException}. + *

+ * + *
+ * PaymentProcessor pp = chequePaymentProcessor.get();
+ * 
+ * + *

+ * The inherited {@link java.lang.Iterable#iterator()} method returns an iterator over contextual references for beans that + * match the required type and required qualifiers and are eligible for injection into the class into which the parent + * Instance was injected. + *

+ * + *
+ * for (PaymentProcessor pp : anyPaymentProcessor)
+ *     pp.test();
+ * 
+ * + * @see javax.inject.Provider#get() + * @see java.lang.Iterable#iterator() + * @see javax.enterprise.util.AnnotationLiteral + * @see javax.enterprise.util.TypeLiteral + * + * @author Gavin King + * @author John Ament + * @author Martin Kouba + * + * @param the required bean type + */ + +public interface Instance extends Iterable, Provider { + + /** + *

+ * Obtains a child Instance for the given additional required qualifiers. + *

+ * + * @param qualifiers the additional required qualifiers + * @return the child Instance + * @throws IllegalArgumentException if passed two instances of the same non repeating qualifier type, or an instance of an annotation that + * is not a qualifier type + * @throws IllegalStateException if the container is already shutdown + */ + Instance select(Annotation... qualifiers); + + /** + *

+ * Obtains a child Instance for the given required type and additional required qualifiers. + *

+ * + * @param the required type + * @param subtype a {@link java.lang.Class} representing the required type + * @param qualifiers the additional required qualifiers + * @return the child Instance + * @throws IllegalArgumentException if passed two instances of the same non repeating qualifier type, or an instance of an annotation that + * is not a qualifier type + * @throws IllegalStateException if the container is already shutdown + */ + Instance select(Class subtype, Annotation... qualifiers); + + /** + *

+ * Obtains a child Instance for the given required type and additional required qualifiers. + *

+ * + * @param the required type + * @param subtype a {@link javax.enterprise.util.TypeLiteral} representing the required type + * @param qualifiers the additional required qualifiers + * @return the child Instance + * @throws IllegalArgumentException if passed two instances of the same non repeating qualifier type, or an instance of an annotation that + * is not a qualifier type + * @throws IllegalStateException if the container is already shutdown + */ + Instance select(TypeLiteral subtype, Annotation... qualifiers); + + /** + *

+ * When called, provides back a Stream of the beans available in this Instance. If no beans are found, it returns an empty + * stream. + *

+ * + * @since 2.0 + * @return a Stream representing the beans associated with this {@link Instance} object + */ + default Stream stream() { + return StreamSupport.stream(this.spliterator(), false); + } + + /** + *

+ * Determines if there is no bean that matches the required type and qualifiers and is eligible for injection into the class + * into which the parent Instance was injected. + *

+ * + * @return true if there is no bean that matches the required type and qualifiers and is eligible for injection + * into the class into which the parent Instance was injected, or false otherwise. + */ + boolean isUnsatisfied(); + + /** + *

+ * Determines if there is more than one bean that matches the required type and qualifiers and is eligible for injection + * into the class into which the parent Instance was injected. + *

+ * + * @return true if there is more than one bean that matches the required type and qualifiers and is eligible for + * injection into the class into which the parent Instance was injected, or false otherwise. + */ + boolean isAmbiguous(); + + /** + *

+ * Determines if there is exactly one bean that matches the required type and qualifiers and is eligible for injection + * into the class into which the parent Instance was injected. + *

+ * + * @since 2.0 + * @return true if there is exactly one bean that matches the required type and qualifiers and is eligible for + * injection into the class into which the parent Instance was injected, or false otherwise. + */ + default boolean isResolvable() { + return !isUnsatisfied() && !isAmbiguous(); + } + + /** + *

+ * When called, the container destroys the instance if the active context object for the scope type of the bean supports + * destroying bean instances. All normal scoped built-in contexts support destroying bean instances. + *

+ * + *

+ * The instance passed should either be a dependent scoped bean instance obtained from the same {@link Instance} object, or + * the client proxy for a normal scoped bean instance. + *

+ * + * + * @since 1.1 + * @param instance the instance to destroy + * @throws UnsupportedOperationException if the active context object for the scope type of the bean does not support + * destroying bean instances + */ + void destroy(T instance); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Intercepted.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Intercepted.java new file mode 100644 index 000000000..67d89cd19 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Intercepted.java @@ -0,0 +1,56 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + *

+ * An interceptor may inject metadata about the bean it is intercepting. + *

+ * + *
+ * @Transactional @Interceptor
+ * public class TransactionInterceptor {
+ * 
+ *    @Inject @Intercepted Bean<?> bean;
+ * 
+ *    @AroundInvoke 
+ *    public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
+ * 
+ * }
+ * 
+ * + * @author Pete Muir + * @since 1.1 + */ + +@Target({ PARAMETER, FIELD }) +@Retention(RUNTIME) +@Documented +@Qualifier +public @interface Intercepted { +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Model.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Model.java new file mode 100644 index 000000000..c2e083c97 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Model.java @@ -0,0 +1,48 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + +/** + *

+ * The built-in stereotype intended for use with beans that define the model layer of an MVC web application architecture such + * as JSF. + *

+ * + * @see javax.enterprise.inject.Stereotype + * @author Gavin King + */ + +@Named +@RequestScoped +@Documented +@Stereotype +@Target({ TYPE, METHOD, FIELD }) +@Retention(RUNTIME) +public @interface Model { +} \ No newline at end of file diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/New.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/New.java new file mode 100644 index 000000000..a352bd22d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/New.java @@ -0,0 +1,115 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.context.Dependent; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Qualifier; + +/** + *

+ * The {@link New} qualifier was deprecated in CDI 1.1. CDI applications are encouraged to inject {@link Dependent} scoped beans + * instead. + *

+ * + *

+ * The @New qualifier allows the application to obtain a new instance of a bean which is not bound to the declared + * scope, but has had dependency injection performed. + *

+ * + *
+ * @Produces @ConversationScoped
+ * @Special Order getSpecialOrder(@New(Order.class) Order order) {
+ *    ...
+ *    return order;
+ * }
+ * 
+ * + *

+ * When the @New qualifier is specified at an injection point and no {@link javax.enterprise.inject.New#value() + * value} member is explicitly specified, the container defaults the {@link javax.enterprise.inject.New#value() value} to the + * declared type of the injection point. So the following injection point has qualifier @New(Order.class): + *

+ * + *
+ * @Produces @ConversationScoped
+ * @Special Order getSpecialOrder(@New Order order) { ... }
+ * 
+ * + * @author Gavin King + * @author Pete Muir + */ + +@Target({ FIELD, PARAMETER, METHOD, TYPE }) +@Retention(RUNTIME) +@Documented +@Qualifier +public @interface New { + /** + *

+ * Specifies the bean class of the new instance. The class must be the bean class of an enabled or disabled bean. The bean + * class need not be deployed in a bean archive. + *

+ * + *

+ * Defaults to the declared type of the injection point if not specified. + *

+ * + * @return the bean class of the new instance + */ + Class value() default New.class; + + /** + * Supports inline instantiation of the {@link New} qualifier. + * + * @author Martin Kouba + * @since 2.0 + */ + public final static class Literal extends AnnotationLiteral implements New { + + public static final Literal INSTANCE = of(New.class); + + private static final long serialVersionUID = 1L; + + private final Class value; + + public static Literal of(Class value) { + return new Literal(value); + } + + private Literal(Class value) { + this.value = value; + } + + public Class value() { + return value; + } + } + + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Produces.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Produces.java new file mode 100644 index 000000000..090096f2b --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Produces.java @@ -0,0 +1,121 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * + *

+ * Identifies a producer method or field. May be applied to a method or field of a bean class. + *

+ * + *

+ * A producer method must be a non-abstract method of a managed bean class or session bean class. A producer method may be + * either static or non-static. If the bean is a session bean, the producer method must be either a business method of the EJB + * or a static method of the bean class. + *

+ * + *
+ * public class Shop {
+ *    @Produces @ApplicationScoped 
+ *    @Catalog @Named("catalog") 
+ *    List<Product> getProducts() { ... }
+ *    ...
+ * }
+ * 
+ * + *

+ * A producer field must be a field of a managed bean class or session bean class. A producer field may be either static or + * non-static. If the bean is a session bean, the producer field must be a static field of the bean class. + *

+ * + *
+ * public class Shop { 
+ *    @Produces @ApplicationScoped 
+ *    @Catalog @Named("catalog") 
+ *    List<Product> products = ...;
+ *    ...
+ * }
+ * 
+ * + *

+ * If a producer method sometimes returns a null value, or if a producer field sometimes contains a null value when accessed, + * then the producer method or field must have scope {@link javax.enterprise.context.Dependent @Dependent}. + *

+ * + *

+ * A producer method return type or producer field type may not be a type variable. + *

+ * + *

+ * If the producer method return type or producer field type is a parameterized type, it must specify an actual type parameter + * or type variable for each type parameter. + *

+ * + *

+ * If the producer method return type or producer field type is a parameterized type with a type variable, it must have scope + * {@link javax.enterprise.context.Dependent @Dependent}. + *

+ * + *

+ * A producer method may have any number of parameters. All producer method parameters are injection points. + *

+ * + *
+ * public class OrderFactory {
+ * 
+ *     @Produces
+ *     @ConversationScoped
+ *     public Order createCurrentOrder(Shop shop, @Selected Product product) {
+ *         Order order = new Order(product, shop);
+ *         return order;
+ *     }
+ * 
+ * }
+ * 
+ * + *

+ * A bean may declare multiple producer methods or fields. + *

+ * + *

+ * Producer methods and fields are not inherited by bean subclasses. + *

+ * + *

+ * Interceptors and decorators may not declare producer methods or fields. + *

+ * + * @see javax.enterprise.inject.Disposes @Disposes + * + * @author Gavin King + * @author Pete Muir + */ + +@Target({ METHOD, FIELD }) +@Retention(RUNTIME) +@Documented +public @interface Produces { +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/ResolutionException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/ResolutionException.java new file mode 100644 index 000000000..a9efb449a --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/ResolutionException.java @@ -0,0 +1,44 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +/** + * Indicates a problem relating to typesafe resolution. + * + * @author Gavin King + */ +public class ResolutionException extends InjectionException { + + private static final long serialVersionUID = -6280627846071966243L; + + public ResolutionException() { + super(); + } + + public ResolutionException(String message, Throwable cause) { + super(message, cause); + } + + public ResolutionException(String message) { + super(message); + } + + public ResolutionException(Throwable cause) { + super(cause); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Specializes.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Specializes.java new file mode 100644 index 000000000..373ef2a77 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Specializes.java @@ -0,0 +1,89 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.event.Event; +import javax.enterprise.util.AnnotationLiteral; + +/** + *

+ * Indicates that a bean directly specializes another bean. May be applied to a bean class or producer method. + *

+ * + *

+ * If a bean directly specializes a second bean, it inherits: + *

+ * + *
    + *
  • all qualifiers of the second bean, and
  • + *
  • the name, if any, of the second bean.
  • + *
+ * + *

+ * If the second bean has a name, the bean may not declare a name using {@link javax.inject.Named @Named}. Furthermore, the + * bean must have all the bean types of the second bean. + *

+ * + *
    + *
  • If a bean class of a managed bean is annotated @Specializes , then the bean class must directly extend the + * bean class of a second managed bean. Then the first managed bean directly specializes the second managed bean.
  • + * + *
  • If a bean class of a session bean is annotated @Specializes , then the bean class must directly extend the + * bean class of a second session bean. Then the first session bean directly specializes the second session bean.
  • + * + *
  • If a producer method is annotated @Specializes, then it must be non-static and directly override another + * producer method. Then the first producer method directly specializes the second producer method.
  • + *
+ * + *

+ * If a bean is specialized by any enabled bean, the first bean is disabled. + *

+ * + * @author Gavin King + * @author Pete Muir + */ + +@Target({ TYPE, METHOD }) +@Retention(RUNTIME) +@Documented +public @interface Specializes { + + /** + * Supports inline instantiation of the {@link Specializes} annotation. + * + * @author Martin Kouba + * @since 2.0 + * @see Instance + * @see Event + */ + public static final class Literal extends AnnotationLiteral implements Specializes { + + private static final long serialVersionUID = 1L; + + public static final Literal INSTANCE = new Literal(); + + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Stereotype.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Stereotype.java new file mode 100644 index 000000000..d4926f744 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Stereotype.java @@ -0,0 +1,137 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + *

+ * Specifies that an annotation type is a stereotype. + *

+ * + *

+ * In many systems, use of architectural patterns produces a set of recurring bean roles. A stereotype allows a framework + * developer to identify such a role and declare some common metadata for beans with that role in a central place. + *

+ * + *

+ * A bean may declare zero, one or multiple stereotypes, by applying the stereotype annotation to the bean class or producer + * method or field. + *

+ * + *

+ * A stereotype encapsulates any combination of: + *

+ * + *
    + *
  • a default scope, and
  • + *
  • a set of interceptor bindings.
  • + *
+ * + *

+ * The default scope of a stereotype is defined by annotating the stereotype with a scope type. A stereotype may declare at most + * one scope. If a bean explicitly declares a scope, any default scopes declared by its stereotypes are ignored. + *

+ * + *
+ * @RequestScoped
+ * @Stereotype
+ * @Target(TYPE)
+ * @Retention(RUNTIME)
+ * public @interface Action {
+ * }
+ * 
+ * + *

+ * The interceptor bindings of a stereotype are defined by annotating the stereotype with the interceptor binding types. A + * stereotype may declare zero, one or multiple interceptor bindings. An interceptor binding declared by a stereotype is + * inherited by any bean that declares that stereotype. + *

+ * + *
+ * @RequestScoped
+ * @Secure
+ * @Transactional
+ * @Stereotype
+ * @Target(TYPE)
+ * @Retention(RUNTIME)
+ * public @interface Action {
+ * }
+ * 
+ * + *

+ * A stereotype may also specify that: + *

+ * + *
    + *
  • all beans with the stereotype have defaulted bean EL names, or that
  • + *
  • all beans with the stereotype are alternatives.
  • + *
+ * + *

+ * A stereotype may declare an empty {@link javax.inject.Named @Named} annotation, which specifies that every bean with the + * stereotype has a defaulted name when a name is not explicitly specified by the bean. + *

+ * + *
+ * @RequestScoped
+ * @Named
+ * @Secure
+ * @Transactional
+ * @Stereotype
+ * @Target(TYPE)
+ * @Retention(RUNTIME)
+ * public @interface Action {
+ * }
+ * 
+ * + *

+ * A stereotype may declare an {@link javax.enterprise.inject.Alternative @Alternative} annotation, which specifies that + * every bean with the stereotype is an alternative. + *

+ * + *
+ * @Alternative
+ * @Stereotype
+ * @Target(TYPE)
+ * @Retention(RUNTIME)
+ * public @interface Mock {
+ * }
+ * 
+ * + *

+ * A stereotype may declare other stereotypes. Stereotype declarations are transitive. A stereotype declared by a second + * stereotype is inherited by all beans and other stereotypes that declare the second stereotype. + *

+ * + * @see javax.enterprise.inject.Model the built-in stereotype @Model + * + * @author Pete Muir + * @author Gavin King + */ + +@Retention(RUNTIME) +@Target(ANNOTATION_TYPE) +@Documented +public @interface Stereotype { +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/TransientReference.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/TransientReference.java new file mode 100644 index 000000000..0e7c7867d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/TransientReference.java @@ -0,0 +1,72 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.event.Event; +import javax.enterprise.util.AnnotationLiteral; + +/** + *

+ * If a parameter annotated with @TransientReference resolves to a dependent scoped bean, then the bean will be + * destroyed after the invocation completes. + *

+ * + *
+ * public class OrderManager {
+ * 
+ *     @Inject 
+ *     public OrderManager(@TransientReference Order order) {
+ *        ...
+ *    
+ *     }
+ * }
+ * 
+ * + * @author Pete Muir + * @since 1.1 + */ + +@Target(PARAMETER) +@Retention(RUNTIME) +@Documented +public @interface TransientReference { + + /** + * Supports inline instantiation of the {@link TransientReference} annotation. + * + * @author Martin Kouba + * @since 2.0 + * @see Instance + * @see Event + */ + public static final class Literal extends AnnotationLiteral implements TransientReference { + + private static final long serialVersionUID = 1L; + + public static final Literal INSTANCE = new Literal(); + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Typed.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Typed.java new file mode 100644 index 000000000..a96e37738 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Typed.java @@ -0,0 +1,95 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.util.AnnotationLiteral; + +/** + *

+ * Restricts the bean types of a bean. May be applied to a bean class or producer method or field. + *

+ * + *
+ * @Typed(Shop.class)
+ * public class BookShop
+ *       extends Business
+ *       implements Shop<Book> {
+ *    ...
+ * }
+ * 
+ * + *

+ * When a @Typed annotation is specified, only the types whose classes are explicitly listed using the + * {@link javax.enterprise.inject.Typed#value() value} member, along with {@link java.lang.Object}, are bean types of the bean. + *

+ * + * @author Pete Muir + * @author Gavin King + * + */ +@Target({ FIELD, METHOD, TYPE }) +@Retention(RUNTIME) +@Documented +public @interface Typed { + /** + *

+ * Selects the bean types of the bean. Every class must correspond to a type in the unrestricted set of bean types of a + * bean. + *

+ * + * @return the classes corresponding to the bean types of the bean + */ + Class[] value() default {}; + + /** + * Supports inline instantiation of the {@link Typed} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ + public final static class Literal extends AnnotationLiteral implements Typed { + + public static final Literal INSTANCE = of(new Class[]{}); + + private static final long serialVersionUID = 1L; + + private final Class[] value; + + public static Literal of(Class[] value) { + return new Literal(value); + } + + private Literal(Class[] value) { + this.value = value; + } + + public Class[] value() { + return value; + } + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/UnproxyableResolutionException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/UnproxyableResolutionException.java new file mode 100644 index 000000000..af7b1c23d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/UnproxyableResolutionException.java @@ -0,0 +1,49 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +/** + *

+ * Indicates that a contextual reference for a bean with a normal scope and a certain bean type cannot be obtained because the + * bean type cannot be proxied by the container. + *

+ * + * @author Pete Muir + * @author Gavin King + */ +public class UnproxyableResolutionException extends ResolutionException { + + private static final long serialVersionUID = 1667539354548135465L; + + public UnproxyableResolutionException() { + super(); + } + + public UnproxyableResolutionException(String message, Throwable throwable) { + super(message, throwable); + } + + public UnproxyableResolutionException(String message) { + super(message); + } + + public UnproxyableResolutionException(Throwable throwable) { + super(throwable); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/UnsatisfiedResolutionException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/UnsatisfiedResolutionException.java new file mode 100644 index 000000000..79f08566f --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/UnsatisfiedResolutionException.java @@ -0,0 +1,48 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +/** + *

+ * Indicates that no bean matches a certain combination of required type and required qualifiers and is eligible for injection + * into a certain class. + *

+ * + * @author Pete Muir + * @author Gavin King + */ +public class UnsatisfiedResolutionException extends ResolutionException { + + private static final long serialVersionUID = 5350603312442756709L; + + public UnsatisfiedResolutionException() { + super(); + } + + public UnsatisfiedResolutionException(String message, Throwable throwable) { + super(message, throwable); + } + + public UnsatisfiedResolutionException(String message) { + super(message); + } + + public UnsatisfiedResolutionException(Throwable throwable) { + super(throwable); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Vetoed.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Vetoed.java new file mode 100644 index 000000000..11ccba438 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/Vetoed.java @@ -0,0 +1,69 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.enterprise.event.Event; +import javax.enterprise.util.AnnotationLiteral; + +/** + *

+ * Veto the processing of the class. Any beans or observer methods defined by this class will not be installed. + *

+ * + *

+ * When placed on package, all beans in the package are prevented from being installed. If packages are split across jars, + * non-portable behavior results. An application can prevent packages being split across jars by sealing the package. + *

+ * + *

+ * No container lifecycle events are fired for classes annotated {@link Vetoed}. + *

+ * + * @author Stuart Douglas + * @since 1.1 + * @see JAR File Specification + * + */ +@Target({ ElementType.TYPE, ElementType.PACKAGE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Vetoed { + + /** + * Supports inline instantiation of the {@link Vetoed} annotation. + * + * @author Martin Kouba + * @since 2.0 + * @see Instance + * @see Event + */ + public static final class Literal extends AnnotationLiteral implements Vetoed { + + private static final long serialVersionUID = 1L; + + public static final Literal INSTANCE = new Literal(); + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/InjectLiteral.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/InjectLiteral.java new file mode 100644 index 000000000..5177999e4 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/InjectLiteral.java @@ -0,0 +1,34 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.literal; + +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Inject; + +/** + * Supports inline instantiation of the {@link Inject} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ +public final class InjectLiteral extends AnnotationLiteral implements Inject { + + public static final InjectLiteral INSTANCE = new InjectLiteral(); + + private static final long serialVersionUID = 1L; + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/NamedLiteral.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/NamedLiteral.java new file mode 100644 index 000000000..458e8f8fe --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/NamedLiteral.java @@ -0,0 +1,49 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.literal; + +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Named; + +/** + * Supports inline instantiation of the {@link Named} qualifier. + * + * @author Pete Muir + * @author Jozef Hartinger + * @since 2.0 + */ +public final class NamedLiteral extends AnnotationLiteral implements Named { + + public static final Named INSTANCE = of(""); + + private static final long serialVersionUID = 1L; + + private final String value; + + public static NamedLiteral of(String value) { + return new NamedLiteral(value); + } + + public String value() { + return value; + } + + private NamedLiteral(String value) { + this.value = value; + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/QualifierLiteral.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/QualifierLiteral.java new file mode 100644 index 000000000..a3bb242bc --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/QualifierLiteral.java @@ -0,0 +1,34 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.literal; + +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Qualifier; + +/** + * Supports inline instantiation of the {@link Qualifier} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ +public final class QualifierLiteral extends AnnotationLiteral implements Qualifier { + + public static final QualifierLiteral INSTANCE = new QualifierLiteral(); + + private static final long serialVersionUID = 1L; + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/SingletonLiteral.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/SingletonLiteral.java new file mode 100644 index 000000000..50061317f --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/literal/SingletonLiteral.java @@ -0,0 +1,34 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2008, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.literal; + +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Singleton; + +/** + * Supports inline instantiation of the {@link Singleton} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ +public final class SingletonLiteral extends AnnotationLiteral implements Singleton { + + public static final SingletonLiteral INSTANCE = new SingletonLiteral(); + + private static final long serialVersionUID = 1L; + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/se/SeContainer.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/se/SeContainer.java new file mode 100644 index 000000000..254531a0d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/se/SeContainer.java @@ -0,0 +1,63 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.se; + +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.BeanManager; + +/** + * Provides access to the current container in Java SE. + * + *

+ * SeContainer implements {@link Instance} and therefore might be used to perform programmatic lookup. + * If no qualifier is passed to {@link #select} method, the @Default qualifier is assumed. + *

+ * + * @author Antoine Sabot-Durand + * @author John D. Ament + * @since 2.0 + */ +public interface SeContainer extends Instance,AutoCloseable { + + + /** + *

+ * Shuts down this SeContainer instance when it is no longer in scope. Implemented from {@link AutoCloseable}, + *

+ * @throws IllegalStateException if the container is already shutdown + */ + @Override + void close(); + + /** + * + * Check if the container is running or was shut down + * + * @return true if called before container shutdown + */ + boolean isRunning(); + + /** + * Get the CDI BeanManager for this container + * + * @return the BeanManager + * @throws IllegalStateException if called when the container is already shutdown + */ + BeanManager getBeanManager(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/se/SeContainerInitializer.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/se/SeContainerInitializer.java new file mode 100644 index 000000000..c8ebf0e7d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/se/SeContainerInitializer.java @@ -0,0 +1,298 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.se; + +import java.lang.annotation.Annotation; +import java.util.Iterator; +import java.util.Map; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; + +import javax.enterprise.inject.spi.Extension; + +/** + * A CDI container initializer for Java SE. An instance may be obtained by calling {@link SeContainerInitializer#newInstance()} + * static method. + *

+ * Typical usage looks like this: + *

+ * + *
+ * SeContainer container = SeContainerInitializer.newInstance().initialize();
+ * container.select(Foo.class).get();
+ * container.close();
+ * 
+ * + *

+ * Since {@link SeContainer} interface implements AutoCloseable: + *

+ * + *
+ * try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
+ *     container.select(Foo.class).get();
+ * }
+ * 
+ * + *

+ * By default, the discovery is enabled so that all beans from all discovered bean archives are considered. However, it's + * possible to define a "synthetic" bean archive, or the set of bean classes and enablement respectively: + *

+ + *
+ * SeContainer container = SeContainerInitializer.newInstance().addBeanClasses(Foo.class, Bar.class).selectAlternatives(Bar.class).initialize());
+ * 
+ * + *

+ * Moreover, it's also possible to disable the discovery completely so that only the "synthetic" bean archive is considered: + *

+ * + *
+ * SeContainer container = SeContainerInitializer.newInstance().disableDiscovery().addBeanClasses(Foo.class, Bar.class).initialize());
+ * 
+ + *

+ * In the same manner, it is possible to explicitly declare interceptors, decorators, extensions and implementation specific + * options using the builder. + *

+ * + *
+ * SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance()
+ *         .disableDiscovery()
+ *         .addPackages(Main.class, Utils.class)
+ *         .enableInterceptors(TransactionalInterceptor.class)
+ *         .addProperty("property", true);
+ * SeContainer container = container.initialize();
+ * 
+ * + * @author Antoine Sabot-Durand + * @author Martin Kouba + * @author John D. Ament + * @since 2.0 + */ +public abstract class SeContainerInitializer { + + /** + * Returns an instance of {@link SeContainerInitializer} + * Each call returns a new instance + * + * @return a new SeContainerInitializer instance. + * @throws IllegalStateException if called in a Java EE container + */ + public static SeContainerInitializer newInstance() { + return findSeContainerInitializer(); + } + + private static SeContainerInitializer findSeContainerInitializer() { + + SeContainerInitializer result; + Iterator iterator = ServiceLoader.load(SeContainerInitializer.class, SeContainerInitializer.class.getClassLoader()).iterator(); + + if (!iterator.hasNext()) { + throw new IllegalStateException("No valid CDI implementation found"); + } + try { + result = iterator.next(); + } catch (ServiceConfigurationError e) { + throw new IllegalStateException("Error while instantiating SeContainerInitializer", e); + } + if (iterator.hasNext()) + throw new IllegalStateException("Two or more CDI implementations found, only one is supported"); + return result; + } + + /** + * Add provided bean classes to the synthetic bean archive. + * + * @param classes classes to add to the synthetic bean archive + * @return self + */ + public abstract SeContainerInitializer addBeanClasses(Class... classes); + + + /** + * All classes from the packages of the specified classes will be added to the set of bean classes for the synthetic bean archive. + *

+ *

+ * Note that the scanning possibilities are limited. Therefore, only directories and jar files from the filesystem are supported. + *

+ *

+ *

+ * Scanning may also have negative impact on SE performance. + *

+ * + * @param packageClasses classes whose packages will be added to the synthetic bean archive + * @return self + */ + public abstract SeContainerInitializer addPackages(Class... packageClasses); + + /** + * Packages of the specified classes will be scanned and found classes will be added to the set of bean classes for the synthetic bean archive.* + *

+ *

+ * Note that the scanning possibilities are limited. Therefore, only directories and jar files from the filesystem are supported. + *

+ *

+ *

+ * Scanning may also have negative impact on SE performance. + *

+ * + * @param scanRecursively should subpackages be scanned or not + * @param packageClasses classes whose packages will be scanned + * @return self + */ + public abstract SeContainerInitializer addPackages(boolean scanRecursively, Class... packageClasses); + + + /** + * All classes from the specified packages will be added to the set of bean classes for the synthetic bean archive. + *

+ *

+ * Note that the scanning possibilities are limited. Therefore, only directories and jar files from the filesystem are supported. + *

+ *

+ *

+ * Scanning may also have negative impact on SE performance. + *

+ * + * @param packages packages that will be added to the synthetic bean archive + * @return self + */ + public abstract SeContainerInitializer addPackages(Package... packages); + + /** + * All classes from the specified packages will be added to the set of bean classes for the synthetic bean archive. + *

+ *

+ * Note that the scanning possibilities are limited. Therefore, only directories and jar files from the filesystem are supported. + *

+ *

+ *

+ * Scanning may also have negative impact on SE performance. + *

+ * + * @param scanRecursively should subpackages be scanned or not + * @param packages packages that will be added to the synthetic bean archive + * @return self + */ + public abstract SeContainerInitializer addPackages(boolean scanRecursively, Package... packages); + + /** + * Add extensions to the set of extensions. + * + * @param extensions extensions to use in the container + * @return self + */ + public abstract SeContainerInitializer addExtensions(Extension... extensions); + + /** + * Add extensions to the set of extensions. + * + * @param extensions extensions class to use in the container + * @return self + */ + @SuppressWarnings("unchecked") + public abstract SeContainerInitializer addExtensions(Class... extensions); + + /** + * Add interceptor classes to the list of enabled interceptors for the synthetic bean archive. + *

+ * This method does not add any class to the set of bean classes of the synthetic bean archive. + *

+ * @param interceptorClasses classes of the interceptors to enable. + * @return self + */ + public abstract SeContainerInitializer enableInterceptors(Class... interceptorClasses); + + /** + * Add decorator classes to the list of enabled decorators for the synthetic bean archive. + *

+ * This method does not add any class to the set of bean classes of the synthetic bean archive. + *

+ * @param decoratorClasses classes of the decorators to enable. + * @return self + */ + public abstract SeContainerInitializer enableDecorators(Class... decoratorClasses); + + /** + * Add alternatives classes to the list of selected alternatives for the synthetic bean archive. + *

+ * This method does not add any class to the set of bean classes of the synthetic bean archive. + *

+ * @param alternativeClasses classes of the alternatives to select + * @return self + */ + public abstract SeContainerInitializer selectAlternatives(Class... alternativeClasses); + + /** + * Add alternative stereotype classes to the list of selected alternative stereotypes for the synthetic bean archive. + *

+ * This method does not add any class to the set of bean classes of the synthetic bean archive. + *

+ * @param alternativeStereotypeClasses alternatives stereotypes to select + * @return self + */ + @SuppressWarnings("unchecked") + public abstract SeContainerInitializer selectAlternativeStereotypes(Class... alternativeStereotypeClasses); + + /** + * Add a configuration property to the container + * + * @param key property name + * @param value property value + * @return self + */ + public abstract SeContainerInitializer addProperty(String key, Object value); + + /** + * Set all the configuration properties. + * Erase previous properties set + * + * @param properties a map containing properties to add + * @return self + */ + public abstract SeContainerInitializer setProperties(Map properties); + + /** + * By default, the discovery is enabled. However, it's possible to disable the discovery completely so that only the "synthetic" bean archive is considered. + * + * @return self + */ + public abstract SeContainerInitializer disableDiscovery(); + + /** + * Set a {@link ClassLoader}. The given {@link ClassLoader} will be scanned automatically for bean archives if scanning is enabled. + * + * @param classLoader the class loader to use + * @return self + */ + public abstract SeContainerInitializer setClassLoader(ClassLoader classLoader); + + /** + *

+ * Initializes a CDI SeContainerInitializer. + *

+ *

+ * Cannot be called within an application server. + *

+ * + * @return the {@link SeContainer} instance associated with the container. + * @throws UnsupportedOperationException if called within an application server + */ + public abstract SeContainer initialize(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AfterBeanDiscovery.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AfterBeanDiscovery.java new file mode 100644 index 000000000..2d76fb53d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AfterBeanDiscovery.java @@ -0,0 +1,142 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.context.spi.Context; +import javax.enterprise.inject.spi.configurator.BeanConfigurator; +import javax.enterprise.inject.spi.configurator.ObserverMethodConfigurator; + +/** + *

+ * The event type of the second event fired by the container when it has fully completed the bean discovery process, validated + * that there are no definition errors relating to the discovered beans, and registered {@link javax.enterprise.inject.spi.Bean} + * and {@link javax.enterprise.inject.spi.ObserverMethod} objects for the discovered beans, but before detecting deployment + * problems. + *

+ *

+ * A portable extension may take advantage of this event to register {@linkplain javax.enterprise.inject.spi.Bean beans}, + * {@linkplain javax.enterprise.inject.spi.Interceptor interceptors}, {@linkplain javax.decorator.Decorator decorators}, + * {@linkplain javax.enterprise.event.Observes observer methods} and {@linkplain javax.enterprise.context custom context} + * objects with the container. + *

+ * + *
+ *     void afterBeanDiscovery(@Observes AfterBeanDiscovery event, BeanManager manager) { ... }
+ * 
+ *

+ * If any observer method of the {@code AfterBeanDiscovery} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + * @author David Allen + * @author Antoine Sabot-Durand + */ +public interface AfterBeanDiscovery { + /** + * Registers a definition error with the container, causing the container to abort deployment after all observers have been + * notified. + * + * @param t The definition error as a {@link java.lang.Throwable} + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addDefinitionError(Throwable t); + + /** + * Fires an event of type {@link javax.enterprise.inject.spi.ProcessBean} containing the given + * {@link javax.enterprise.inject.spi.Bean} and then registers the {@link javax.enterprise.inject.spi.Bean} with the + * container, thereby making a bean which is not an interceptor nor a decorator available for injection into other beans. + * The given {@link javax.enterprise.inject.spi.Bean} may implement {@link javax.enterprise.inject.spi.Interceptor} or {@link javax.decorator.Decorator}. + * + * @param bean The bean to add to the deployment + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addBean(Bean bean); + + /** + * + * Obtains a new {@link BeanConfigurator} to configure a new {@link Bean} and add it at the end of the observer invocation. + * It will then fire an event of type {@link javax.enterprise.inject.spi.ProcessBean} containing the built + * {@link javax.enterprise.inject.spi.Bean} from this configuration and then register it with the + * container, thereby making it available for injection into other beans. + * + * Each call returns a new BeanConfigurator. + * + * @return a non reusable {@link BeanConfigurator} to configure the bean to add + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public BeanConfigurator addBean(); + + /** + * Fires an event of type {@link javax.enterprise.inject.spi.ProcessObserverMethod} containing the given + * {@link javax.enterprise.inject.spi.ObserverMethod} and then registers the + * {@link javax.enterprise.inject.spi.ObserverMethod} with the container, thereby making it available for event + * notifications. + * + * @param observerMethod The custom observer method to add to the deployment + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addObserverMethod(ObserverMethod observerMethod); + + /** + * obtains a new {@link ObserverMethodConfigurator} to configure a new {@link ObserverMethod} and add it at the end of the observer invocation. + * It will then fire an event of type {@link javax.enterprise.inject.spi.ProcessObserverMethod} containing the built + * {@link javax.enterprise.inject.spi.ObserverMethod} from this configuration and then registers the + * {@link javax.enterprise.inject.spi.ObserverMethod} with the container, thereby making it available for event + * notifications. + * + * Each call returns a new ObserverMethodConfigurator. + * + * @return a non reusable {@link ObserverMethodConfigurator} instance + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public ObserverMethodConfigurator addObserverMethod(); + + /** + * Registers a custom {@link javax.enterprise.context.spi.Context} object with the container. + * + * @param context The custom context to add to the deployment + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addContext(Context context); + + /** + * Obtain the {@link AnnotatedType} that may be used to read the annotations of the given class or interface as defined + * during container initialization. + * + * @param the class or interface + * @param type the {@link java.lang.Class} object + * @param id the type identifier. If null, the fully qualifier class name of type is used + * @return the {@link AnnotatedType} + * @throws IllegalStateException if called outside of the observer method invocation + * @since 1.1 + */ + public AnnotatedType getAnnotatedType(Class type, String id); + + /** + * Obtain the {@link AnnotatedType}s that may be used to read the annotations of the given class or interface as defined + * during container initialization. + * + * @param the class or interface + * @param type the {@link java.lang.Class} object + * @return the {@link AnnotatedType}s + * @throws IllegalStateException if called outside of the observer method invocation + * @since 1.1 + */ + public Iterable> getAnnotatedTypes(Class type); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AfterDeploymentValidation.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AfterDeploymentValidation.java new file mode 100644 index 000000000..d19d7083d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AfterDeploymentValidation.java @@ -0,0 +1,42 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The event type of the third event fired by the container after it has validated that there are no deployment problems and + * before creating contexts or processing requests. If any observer method of the {@code AfterDeploymentValidation} event throws + * an exception, the exception is treated as a deployment problem by the container. + *

+ *

+ * No requests will be processed by the deployment until all observers of this event return. + *

+ * + * @author David Allen + */ +public interface AfterDeploymentValidation { + + /** + * Registers a deployment problem with the container, causing the container to abort deployment after all observers have + * been notified. + * + * @param t The deployment problem as a {@link java.lang.Throwable} + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addDeploymentProblem(Throwable t); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AfterTypeDiscovery.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AfterTypeDiscovery.java new file mode 100644 index 000000000..debb0fa12 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AfterTypeDiscovery.java @@ -0,0 +1,111 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator; +import java.util.List; + +/** + *

+ * This event type is thrown by the container after type discovery is complete. If any observer method of the + * {@code AfterTypeDiscovery} event throws an exception, the exception is treated as a definition error by the container. + *

+ *

+ * Any observer of this event is permitted to add classes to, or remove classes from, the list of alternatives, list of + * interceptors or list of decorators. The container will use the final values of these lists, after all observers have been + * called, to determine the enabled alternatives, interceptors, and decorators for application. + * Changes made to these lists after the invocation of the last observer method of the {@code AfterTypeDiscovery} are ignored. + *

+ * + * @author Pete Muir + * @author Antoine Sabot-Durand + * @since 1.1 + */ +public interface AfterTypeDiscovery { + + /** + * @return the list of enabled alternatives for the application, sorted by priority in ascending order. Alternatives enabled for a bean archive are not included. + * @throws IllegalStateException if called outside of the observer method invocation + */ + public List> getAlternatives(); + + /** + * @return the list of enabled interceptors for the application, sorted by priority in ascending order. Interceptors enabled for a bean archive are not included. + * @throws IllegalStateException if called outside of the observer method invocation + */ + public List> getInterceptors(); + + /** + * @return the list of enabled decorators for the application, sorted by priority in ascending order. Decorators enabled for a bean archive are not included. + * @throws IllegalStateException if called outside of the observer method invocation + */ + public List> getDecorators(); + + /** + *

+ * Adds a given {@link javax.enterprise.inject.spi.AnnotatedType} to the set of types which will be scanned during bean + * discovery. + *

+ * + *

+ * Thanks to the id parameter, this method allows multiple annotated types, based on the same underlying type, to be defined. {@link AnnotatedType}s + * discovered by the container use the fully qualified class name of {@link AnnotatedType#getJavaClass()} to identify the + * type. + *

+ * + *

+ * {@link AfterBeanDiscovery#getAnnotatedType(Class, String)} and {@link AfterBeanDiscovery#getAnnotatedTypes(Class)} allows + * annotated types to be obtained by identifier. + *

+ * + * @param type The {@link javax.enterprise.inject.spi.AnnotatedType} to add for later scanning + * @param id the identifier used to distinguish this AnnotatedType from an other one based on the same underlying type + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addAnnotatedType(AnnotatedType type, String id); + + /** + *

+ * Obtains a new {@link AnnotatedTypeConfigurator} to configure a new {@link javax.enterprise.inject.spi.AnnotatedType} and + * add it to the set of types which will be scanned during bean discovery at the end of the observer invocation. + * Calling this method multiple times will return a new AnnotatedTypeConfigurator. + *

+ * + *

+ * Thanks to the id parameter, this method allows multiple annotated types, based on the same underlying type, to be defined. {@link AnnotatedType}s + * discovered by the container use the fully qualified class name of {@link AnnotatedType#getJavaClass()} to identify the + * type. + *

+ * + *

+ * {@link AfterBeanDiscovery#getAnnotatedType(Class, String)} and {@link AfterBeanDiscovery#getAnnotatedTypes(Class)} allows + * annotated types to be obtained by identifier. + *

+ * + * Each call returns a new AnnotatedTypeConfigurator. + * + * + * @param type class used to initialized the type and annotations on the {@link AnnotatedTypeConfigurator} + * @param id the identifier used to distinguish this AnnotatedType from an other one based on the same underlying type + * @return a non reusable {@link AnnotatedTypeConfigurator} to configure the new AnnotatedType + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public AnnotatedTypeConfigurator addAnnotatedType(Class type, String id); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Annotated.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Annotated.java new file mode 100644 index 000000000..9fcea98a2 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Annotated.java @@ -0,0 +1,108 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Type; +import java.util.Set; + +/** + *

+ * Represents a Java program element that can be annotated. + *

+ * + * @see java.lang.reflect.AnnotatedElement + * + * @author Gavin King + * @author Pete Muir + * @author Clint Popetz + * @author John D. Ament + * + */ +public interface Annotated { + + /** + *

+ * Get the type of the annotated program element. + *

+ * + * @return the type of the annotated program element + */ + Type getBaseType(); + + /** + *

+ * Get all types to which the base type should be considered assignable. + *

+ * + * @return a set of all types to which the base type should be considered assignable + */ + Set getTypeClosure(); + + /** + *

+ * Get program element annotation of a certain annotation type. + * + * The behavior of this method is intended to be the same behavior as {@link AnnotatedElement#getAnnotation(Class)}, + * where repeatable annotations are not supported. + *

+ * + * @param the type of the annotation + * @param annotationType the class of the annotation type + * @return the first program element annotation of the given annotation type, or a null value + */ + T getAnnotation(Class annotationType); + + /** + *

+ * Get program element annotations of a certain annotation type. + *

+ * This method returns back all annotations, including repeatable annotations of this type. + * + * The behavior of this method is intended to be the same behavior as {@link AnnotatedElement#getAnnotationsByType(Class)}, + * where repeatable annotations are supported. + *

+ * + * @param the type of the annotation + * @param annotationType the class of the annotation type + * @return the program element annotations of the given annotation type, or an empty collection + */ + Set getAnnotations(Class annotationType); + + /** + *

+ * Get all annotations of the program element. + *

+ * + * @return all annotations of the program element, or an empty set if no annotations are present + */ + Set getAnnotations(); + + /** + *

+ * Determine if the program element has an annotation of a certain annotation type. + * + * The behavior of this method is similar to {@link AnnotatedElement#isAnnotationPresent(Class)} for the underlying + * program element. + * + * @param annotationType the annotation type to check for + * @return true if the program element has an annotation of the given annotation type, or false otherwise + */ + boolean isAnnotationPresent(Class annotationType); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedCallable.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedCallable.java new file mode 100644 index 000000000..c744b0562 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedCallable.java @@ -0,0 +1,42 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.util.List; + +/** + *

+ * Represents a callable member of a Java type. + *

+ * + * @author Gavin King + * @author Pete Muir + * + * @param the declaring type + */ +public interface AnnotatedCallable extends AnnotatedMember { + + /** + *

+ * Get the parameters of the callable member. + *

+ * + * @return the parameters + */ + public List> getParameters(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedConstructor.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedConstructor.java new file mode 100644 index 000000000..f5a0b380a --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedConstructor.java @@ -0,0 +1,53 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.util.LinkedHashSet; +import java.util.Set; + +import static java.util.Arrays.asList; + +/** + *

+ * Represents a constructor of a Java class. + *

+ * + * @author Gavin King + * @author Pete Muir + * + * @param the declaring class + * @see Constructor + */ +public interface AnnotatedConstructor extends AnnotatedCallable { + + /** + *

+ * Get the underlying {@link Constructor}. + *

+ * + * @return the constructor + */ + public Constructor getJavaMember(); + + @Override default Set getAnnotations(Class annotationType) { + T[] annotationsByType = getJavaMember().getAnnotationsByType(annotationType); + return new LinkedHashSet<>(asList(annotationsByType)); + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedField.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedField.java new file mode 100644 index 000000000..330e30058 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedField.java @@ -0,0 +1,52 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.LinkedHashSet; +import java.util.Set; + +import static java.util.Arrays.asList; + +/** + *

+ * Represents a field of a Java class. + *

+ * + * @author Gavin King + * @author Pete Muir + * + * @param the declaring type + * @see Field + */ +public interface AnnotatedField extends AnnotatedMember { + + /** + *

+ * Get the underlying {@link Field}. + *

+ * + * @return the {@link Field} + */ + public Field getJavaMember(); + + @Override default Set getAnnotations(Class annotationType) { + T[] annotationsByType = getJavaMember().getAnnotationsByType(annotationType); + return new LinkedHashSet<>(asList(annotationsByType)); + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedMember.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedMember.java new file mode 100644 index 000000000..9fa366878 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedMember.java @@ -0,0 +1,60 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.reflect.Member; + +/** + *

+ * Represents a member of a Java type. + *

+ * + * @author Gavin King + * @author Pete Muir + * + * @param the declaring type + * @see Member + */ +public interface AnnotatedMember extends Annotated { + /** + *

+ * Get the underlying {@link Member}. + *

+ * + * @return the {@link Member} + */ + public Member getJavaMember(); + + /** + *

+ * Determines if the member is static. + *

+ * + * @return true if the member is static + */ + public boolean isStatic(); + + /** + *

+ * Get the {@linkplain AnnotatedType type} which defines this member. + *

+ * + * @return the type which defines this member + */ + public AnnotatedType getDeclaringType(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedMethod.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedMethod.java new file mode 100644 index 000000000..6d6e98f00 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedMethod.java @@ -0,0 +1,52 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.LinkedHashSet; +import java.util.Set; + +import static java.util.Arrays.asList; + +/** + *

+ * Represents a method of a Java type. + *

+ * + * @author Gavin King + * @author Pete Muir + * + * @param the declaring type + * @see Method + */ +public interface AnnotatedMethod extends AnnotatedCallable { + + /** + *

+ * Get the underlying {@link Method}. + *

+ * + * @return the {@link Method} + */ + public Method getJavaMember(); + + @Override default Set getAnnotations(Class annotationType) { + T[] annotationsByType = getJavaMember().getAnnotationsByType(annotationType); + return new LinkedHashSet<>(asList(annotationsByType)); + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedParameter.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedParameter.java new file mode 100644 index 000000000..3fa62d5b3 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedParameter.java @@ -0,0 +1,79 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Executable; +import java.lang.reflect.Member; +import java.lang.reflect.Parameter; +import java.util.LinkedHashSet; +import java.util.Set; + +import static java.util.Arrays.asList; + +/** + *

+ * Represents a parameter of a method or constructor. + *

+ * + * @author Gavin King + * @author Pete Muir + * @author Jozef Hartinger + * + * @param the type that declares the method or constructor + */ +public interface AnnotatedParameter extends Annotated { + + /** + *

+ * Get the position of the parameter in the method or constructor argument list. + *

+ * + * @return the position of the parameter + */ + public int getPosition(); + + /** + *

+ * Get the declaring {@linkplain AnnotatedCallable method or constructor}. + *

+ * + * @return the declaring callable + */ + public AnnotatedCallable getDeclaringCallable(); + + /** + * Get the underlying {@link Parameter}. + * + * @return the {@link Parameter} + */ + default Parameter getJavaParameter() { + Member member = getDeclaringCallable().getJavaMember(); + if (!(member instanceof Executable)) { + throw new IllegalStateException("Parameter does not belong to an executable: " + member); + } + Executable executable = (Executable) member; + return executable.getParameters()[getPosition()]; + } + + @Override default Set getAnnotations(Class annotationType) { + T[] annotationsByType = getJavaParameter().getAnnotationsByType(annotationType); + return new LinkedHashSet<>(asList(annotationsByType)); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedType.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedType.java new file mode 100644 index 000000000..d0dab96f2 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/AnnotatedType.java @@ -0,0 +1,80 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.util.LinkedHashSet; +import java.util.Set; + +import static java.util.Arrays.asList; + +/** + *

+ * Represents a Java class or interface. + *

+ * + * @author Gavin King + * @author Pete Muir + * + * @param the type + * @see java.lang.Class + */ +public interface AnnotatedType extends Annotated { + + /** + *

+ * Get the underlying {@link java.lang.Class}. + *

+ * + * @return the {@link java.lang.Class} + */ + public Class getJavaClass(); + + /** + *

+ * Get the {@linkplain AnnotatedConstructor constructors} of the type. If an empty set is returned, a default constructor + * with no parameters will be assumed. + *

+ * + * @return the constructors, or an empty set if none are defined + */ + public Set> getConstructors(); + + /** + *

+ * Get the {@linkplain AnnotatedMethod methods} of the type. + *

+ * + * @return the methods, or an empty set if none are defined + */ + public Set> getMethods(); + + /** + *

+ * Get the {@linkplain AnnotatedField fields} of the type. + *

+ * + * @return the fields, or an empty set if none are defined + */ + public Set> getFields(); + + @Override default Set getAnnotations(Class annotationType) { + T[] annotationsByType = getJavaClass().getAnnotationsByType(annotationType); + return new LinkedHashSet<>(asList(annotationsByType)); + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Bean.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Bean.java new file mode 100644 index 000000000..867cc2da9 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Bean.java @@ -0,0 +1,66 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.util.Set; + +import javax.enterprise.context.spi.Contextual; +import javax.enterprise.context.spi.CreationalContext; + +/** + *

+ * Represents an {@linkplain javax.enterprise.inject enabled bean}. This interface defines everything the container needs to + * manage instances of the bean. + *

+ * + * @author Gavin King + * @author David Allen + * @param the class of the bean instance + */ +public interface Bean extends Contextual, BeanAttributes { + + /** + * The bean {@linkplain Class class} of the managed bean or session bean or of the bean that declares the producer method or + * field. + * + * @return the bean {@linkplain Class class} + */ + public Class getBeanClass(); + + /** + * Obtains the {@link javax.enterprise.inject.spi.InjectionPoint} objects representing injection points of the bean, that + * will be validated by the container at initialization time. + * + * @return the set of {@linkplain javax.enterprise.inject.spi.InjectionPoint injection points} of the bean + */ + public Set getInjectionPoints(); + + /** + *

+ * Determines if {@link javax.enterprise.context.spi.Contextual#create(CreationalContext)} sometimes return a null value. + *

+ * + *

+ * As of CDI 1.1 this method is deprecated and can safely always return false. + *

+ * + * @return true if the {@code create()} method may return a null value, and false otherwise + */ + public boolean isNullable(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeanAttributes.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeanAttributes.java new file mode 100644 index 000000000..bd945340b --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeanAttributes.java @@ -0,0 +1,79 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Set; + +/** + * The BeanAttributes interface exposes the basic attributes of a bean. + * + * @author Pete Muir + * @since 1.1 + * @param the class of the bean instance + */ +public interface BeanAttributes { + + /** + * Obtains the {@linkplain javax.enterprise.inject bean types} of the bean. + * + * @return the {@linkplain javax.enterprise.inject bean types} + */ + public Set getTypes(); + + /** + * Obtains the {@linkplain javax.inject.Qualifier qualifiers} of the bean. + * + * @return the {@linkplain javax.inject.Qualifier qualifiers} + */ + public Set getQualifiers(); + + /** + * Obtains the {@linkplain javax.enterprise.context scope} of the bean. + * + * @return the {@linkplain javax.enterprise.context scope} + */ + public Class getScope(); + + /** + * Obtains the {@linkplain javax.enterprise.inject EL name} of a bean, if it has one. + * + * @return the {@linkplain javax.enterprise.inject EL name} + */ + public String getName(); + + /** + * Obtains the {@linkplain javax.enterprise.inject.Stereotype stereotypes} of the bean. + * + * @return the set of {@linkplain javax.enterprise.inject.Stereotype stereotypes} + */ + public Set> getStereotypes(); + + /** + * Determines if the bean is an {@linkplain javax.enterprise.inject.Alternative alternative}. + * + * A custom implementation of {@link Bean} may implement {@link Prioritized} in order to be selected for the application. + * {@link Prioritized#getPriority()} determines the priority used to resolve ambiguities. + * + * @return true if the bean is an {@linkplain javax.enterprise.inject.Alternative alternative}, and false + * otherwise. + */ + public boolean isAlternative(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeanManager.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeanManager.java new file mode 100644 index 000000000..29c62af52 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeanManager.java @@ -0,0 +1,663 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, 2013 Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.el.ELResolver; +import javax.el.ExpressionFactory; +import javax.enterprise.context.ContextNotActiveException; +import javax.enterprise.context.spi.Context; +import javax.enterprise.context.spi.Contextual; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.event.Event; +import javax.enterprise.event.ObserverException; +import javax.enterprise.inject.AmbiguousResolutionException; +import javax.enterprise.inject.InjectionException; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.UnsatisfiedResolutionException; +import javax.enterprise.util.Nonbinding; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Set; + +/** + *

+ * Allows a portable extension to interact directly with the container. Provides operations for obtaining contextual references + * for beans, along with many other operations of use to portable extensions. + *

+ * + *

+ * Any bean may obtain an instance of BeanManager by injecting it: + *

+ * + *
+ * @Inject
+ * BeanManager manager;
+ * 
+ * + *

+ * Java EE components may obtain an instance of BeanManager from {@linkplain javax.naming JNDI} by looking up the name + * {@code java:comp/BeanManager}. + *

+ * + *

+ * Most operations of BeanManager may be called at any time during the execution of the application. + *

+ *

+ * However, the following operations must not be called before the {@link AfterBeanDiscovery} event is fired: + *

+ *
    + *
  • {@link #getBeans(String)},
  • + *
  • {@link #getBeans(java.lang.reflect.Type, java.lang.annotation.Annotation...)},
  • + *
  • {@link #getPassivationCapableBean(String)},
  • + *
  • {@link #resolve(java.util.Set)},
  • + *
  • {@link #resolveDecorators(java.util.Set, java.lang.annotation.Annotation...)},
  • + *
  • {@link #resolveInterceptors(InterceptionType, java.lang.annotation.Annotation...)},
  • + *
  • {@link #resolveObserverMethods(Object, java.lang.annotation.Annotation...)},
  • + *
  • {@link #validate(InjectionPoint)},
  • + *
  • {@link #createInstance()}
  • + *
+ *

+ * and the following operations must not be called before the {@link AfterDeploymentValidation} event is fired: + *

+ *
    + *
  • {@link #getReference(Bean, java.lang.reflect.Type, javax.enterprise.context.spi.CreationalContext)},
  • + *
  • {@link #getInjectableReference(InjectionPoint, javax.enterprise.context.spi.CreationalContext)},
  • + *
+ *

+ * or the container will throw an Exception. + *

+ * + * @author Gavin King + * @author Pete Muir + * @author Clint Popetz + * @author David Allen + * @author Antoine Sabot-Durand + */ +public interface BeanManager { + + + /** + *

+ * Obtains a contextual reference for a certain {@linkplain Bean bean} and a certain bean type of the bean. + *

+ * + * @param bean the {@link Bean} object representing the bean + * @param beanType a bean type that must be implemented by any client proxy that is returned + * @param ctx a {@link javax.enterprise.context.spi.CreationalContext} that may be used to destroy any object with scope + * {@link javax.enterprise.context.Dependent} that is created + * @return a contextual reference representing the bean + * @throws IllegalArgumentException if the given type is not a bean type of the given bean + * @throws IllegalStateException if called during application initialization, before the {@link AfterDeploymentValidation} + * event is fired. + */ + public Object getReference(Bean bean, Type beanType, CreationalContext ctx); + + /** + *

+ * Obtains an injectable reference for a certain {@linkplain InjectionPoint injection point}. + *

+ * + * @param ij the target injection point + * @param ctx a {@link javax.enterprise.context.spi.CreationalContext} that may be used to destroy any object with scope + * {@link javax.enterprise.context.Dependent} that is created + * @return the injectable reference + * @throws UnsatisfiedResolutionException if typesafe resolution results in an unsatisfied dependency + * @throws AmbiguousResolutionException typesafe resolution results in an unresolvable ambiguous dependency + * @throws IllegalStateException if called during application initialization, before the {@link AfterDeploymentValidation} + * event is fired. + */ + public Object getInjectableReference(InjectionPoint ij, CreationalContext ctx); + + /** + * Obtain an instance of a {@link javax.enterprise.context.spi.CreationalContext} for the given + * {@linkplain javax.enterprise.context.spi.Contextual contextual type}, or for a non-contextual object. + * + * @param contextual the {@link javax.enterprise.context.spi.Contextual}, or a null value in the case of a non-contextual + * object + * @return the new {@link javax.enterprise.context.spi.CreationalContext} + */ + public CreationalContext createCreationalContext(Contextual contextual); + + /** + * Return the set of beans which have the given required type and qualifiers and are available for injection in the module + * or library containing the class into which the BeanManager was injected or the Java EE component from whose JNDI + * environment namespace the BeanManager was obtained, according to the rules of typesafe resolution. If no + * qualifiers are given, the {@linkplain javax.enterprise.inject.Default default qualifier} is assumed. + *

+ * Note that when called during invocation of an {@link AfterBeanDiscovery} event observer, + * this method will only return beans discovered by the container before the {@link AfterBeanDiscovery} event is fired. + * + * @param beanType the required bean type + * @param qualifiers the required qualifiers + * @return the resulting set of {@linkplain Bean beans} + * @throws IllegalArgumentException if the given type represents a type variable + * @throws IllegalArgumentException if two instances of the same non repeating qualifier type are given + * @throws IllegalArgumentException if an instance of an annotation that is not a qualifier type is given + * @throws IllegalStateException if called during application initialization, before the {@link AfterBeanDiscovery} + * event is fired. + */ + public Set> getBeans(Type beanType, Annotation... qualifiers); + + /** + * Return the set of beans which have the given EL name and are available for injection in the module or library containing + * the class into which the BeanManager was injected or the Java EE component from whose JNDI environment namespace + * the BeanManager was obtained, according to the rules of EL name resolution. + *

+ * Note that when called during invocation of an {@link AfterBeanDiscovery} event observer, + * this method will only return beans discovered by the container before the {@link AfterBeanDiscovery} event is fired. + * + * @param name the EL name + * @return the resulting set of {@linkplain Bean beans} + * @throws IllegalStateException if called during application initialization, before the {@link AfterBeanDiscovery} + * event is fired. + */ + public Set> getBeans(String name); + + /** + * Returns the {@link javax.enterprise.inject.spi.PassivationCapable} bean with the given identifier. + * + *

+ * Note that when called during invocation of an {@link AfterBeanDiscovery} event observer, + * this method will only return beans discovered by the container before the {@link AfterBeanDiscovery} event is fired. + * + * @param id the identifier + * @return a {@link Bean} that implements {@link javax.enterprise.inject.spi.PassivationCapable} and has the given + * identifier, or a null value if there is no such bean + * @throws IllegalStateException if called during application initialization, before the {@link AfterBeanDiscovery} + * event is fired. + */ + public Bean getPassivationCapableBean(String id); + + /** + * Apply the ambiguous dependency resolution rules to a set of {@linkplain Bean beans}. + * + *

+ Note that when called during invocation of an {@link AfterBeanDiscovery} event observer, + * this method will only return beans discovered by the container before the {@link AfterBeanDiscovery} event is fired. + * + * @param a common type of the beans + * @param beans a set of {@linkplain Bean beans} of the given type + * @return the resolved bean, or null if null or an empty set is passed + * @throws AmbiguousResolutionException if the ambiguous dependency resolution rules fail + * @throws IllegalStateException if called during application initialization, before the {@link AfterBeanDiscovery} + * event is fired. + */ + public Bean resolve(Set> beans); + + /** + * Validate a certain {@linkplain InjectionPoint injection point}. + * + *

+ * Note that when called during invocation of an {@link AfterBeanDiscovery} event observer, + * this method will only validate injection points discovered by the container before the {@link AfterBeanDiscovery} + * event is fired. + * + * @param injectionPoint the {@linkplain InjectionPoint injection point} to validate + * @throws InjectionException if there is a deployment problem (for example, an unsatisfied or unresolvable ambiguous + * dependency) associated with the injection point + * @throws IllegalStateException if called during application initialization, before the {@link AfterBeanDiscovery} + * event is fired. + */ + public void validate(InjectionPoint injectionPoint); + + /** + * Fire an event and notify observers. + * + *

+ * This method is deprecated from CDI 2.0 and {@link #getEvent())} should be used instead. + *

+ * + * @param event the event object + * @param qualifiers the event qualifiers + * @throws IllegalArgumentException if the runtime type of the event object contains a type variable + * @throws IllegalArgumentException if two instances of the same non repeating qualifier type are given + * @throws IllegalArgumentException if an instance of an annotation that is not a qualifier type is given + * @throws IllegalArgumentException if the runtime type of the event object is assignable to the type of a container + * lifecycle event + * @throws ObserverException if a notified observer throws a checked exception, it will be wrapped and rethrown as an + * (unchecked) {@link ObserverException} + */ + public void fireEvent(Object event, Annotation... qualifiers); + + /** + * Return an ordered set of {@linkplain ObserverMethod observer methods} for an event. + * + *

+ * Note that when called during invocation of an {@link AfterBeanDiscovery} event observer, + * this method will only return observers discovered by the container before the {@link AfterBeanDiscovery} event is fired. + * + * @param the type of the event + * @param event the event object + * @param qualifiers the event qualifiers + * @return the resulting set of {@linkplain ObserverMethod observer methods} + * @throws IllegalArgumentException if the runtime type of the event object contains a type variable + * @throws IllegalArgumentException if two instances of the same non repeating qualifier type are given + * @throws IllegalArgumentException if an instance of an annotation that is not a qualifier type is given + * @throws IllegalStateException if called during application initialization, before the {@link AfterBeanDiscovery} + * event is fired. + */ + public Set> resolveObserverMethods(T event, Annotation... qualifiers); + + /** + * Return an ordered list of {@linkplain Decorator decorators} for a set of bean types and a set of qualifiers and which are + * enabled in the module or library containing the class into which the BeanManager was injected or the Java EE + * component from whose JNDI environment namespace the BeanManager was obtained. + * + *

+ * Note that when called during invocation of an {@link AfterBeanDiscovery} event observer, + * this method will only return decorators discovered by the container before the {@link AfterBeanDiscovery} event is fired. + * + * @param types the set of bean types of the decorated bean + * @param qualifiers the qualifiers declared by the decorated bean + * @return the resulting set of {@linkplain Decorator decorators} + * @throws IllegalArgumentException if the set of bean types is empty + * @throws IllegalArgumentException if an annotation which is not a binding type is passed + * @throws IllegalArgumentException if two instances of the same binding type are passed + * @throws IllegalStateException if called during application initialization, before the {@link AfterBeanDiscovery} + * event is fired. + */ + public List> resolveDecorators(Set types, Annotation... qualifiers); + + /** + * Return an ordered list of enabled {@linkplain Interceptor interceptors} for a set of interceptor bindings and a type of + * interception and which are enabled in the module or library containing the class into which the BeanManager was + * injected or the Java EE component from whose JNDI environment namespace the BeanManager was obtained. + * + *

+ * Note that when called during invocation of an {@link AfterBeanDiscovery} event observer, + * this method will only return interceptors discovered by the container before the {@link AfterBeanDiscovery} event is + * fired. + * + * @param type the type of the interception + * @param interceptorBindings the interceptor bindings + * @return the resulting set of {@linkplain Interceptor interceptors} + * @throws IllegalArgumentException if no interceptor binding type is given + * @throws IllegalArgumentException if two instances of the same interceptor binding type are given + * @throws IllegalArgumentException if an instance of an annotation that is not an interceptor binding type is given + * @throws IllegalStateException if called during application initialization, before the {@link AfterBeanDiscovery} + * event is fired. + */ + public List> resolveInterceptors(InterceptionType type, Annotation... interceptorBindings); + + /** + * Test the given annotation type to determine if it is a {@linkplain javax.enterprise.context scope type}. + * + * @param annotationType the annotation type + * @return true if the annotation type is a {@linkplain javax.enterprise.context scope type} + */ + public boolean isScope(Class annotationType); + + /** + * Test the given annotation type to determine if it is a {@linkplain javax.enterprise.context normal scope type}. + * + * @param annotationType the annotation type + * @return true if the annotation type is a {@linkplain javax.enterprise.context normal scope type} + */ + public boolean isNormalScope(Class annotationType); + + /** + * Test the given annotation type to determine if it is a passivating {@linkplain javax.enterprise.context scope type}. + * + * @param annotationType the annotation type + * @return true if the annotation type is a passivating scope type + */ + public boolean isPassivatingScope(Class annotationType); + + /** + * Test the given annotation type to determine if it is a {@linkplain javax.inject.Qualifier qualifier type}. + * + * @param annotationType the annotation type + * @return true if the annotation type is a {@linkplain javax.inject.Qualifier qualifier type} + */ + public boolean isQualifier(Class annotationType); + + /** + * Test the given annotation type to determine if it is an {@linkplain javax.interceptor.InterceptorBinding interceptor + * binding type} . + * + * @param annotationType the annotation to test + * @return true if the annotation type is a {@linkplain javax.interceptor.InterceptorBinding interceptor binding + * type} + */ + public boolean isInterceptorBinding(Class annotationType); + + /** + * Test the given annotation type to determine if it is a {@linkplain javax.enterprise.inject.Stereotype stereotype}. + * + * @param annotationType the annotation type + * @return true if the annotation type is a {@linkplain javax.enterprise.inject.Stereotype stereotype} + */ + public boolean isStereotype(Class annotationType); + + /** + * Obtains the set of meta-annotations for a certain {@linkplain javax.interceptor.InterceptorBinding interceptor binding + * type} . + * + * @param bindingType the {@linkplain javax.interceptor.InterceptorBinding interceptor binding type} + * @return the set of meta-annotations + */ + public Set getInterceptorBindingDefinition(Class bindingType); + + /** + * Obtains meta-annotations for a certain {@linkplain javax.enterprise.inject.Stereotype stereotype}. + * + * @param stereotype the {@linkplain javax.enterprise.inject.Stereotype stereotype} + * @return the set of meta-annotations + */ + public Set getStereotypeDefinition(Class stereotype); + + /** + * Determine if two qualifiers are considered equivalent for the purposes of typesafe resolution, taking into account any + * members annotated with {@link Nonbinding}. + * + * @param qualifier1 a qualifier to check + * @param qualifier2 a qualifier to check + * @return true if the two qualifiers are equivalent, otherwise false + * @since 1.1 + */ + public boolean areQualifiersEquivalent(Annotation qualifier1, Annotation qualifier2); + + /** + * Determine if two interceptor bindings are considered equivalent for the purposes of typesafe resolution, taking into + * account any members annotated with {@link Nonbinding}. + * + * @param interceptorBinding1 an interceptor binding to check + * @param interceptorBinding2 an interceptor binding to check + * @return true if the two interceptor bindings are equivalent, otherwise false + * @since 1.1 + */ + public boolean areInterceptorBindingsEquivalent(Annotation interceptorBinding1, Annotation interceptorBinding2); + + /** + * Determine the hash code of a qualifier, using the JDK algorithm for determining an annotation hash code, ignoring any + * members annotated with {@link Nonbinding}. + * + * @param qualifier the qualifier to consider + * @return the hashCode for the qualifier + * @since 1.1 + */ + public int getQualifierHashCode(Annotation qualifier); + + /** + * Determine the hash code of an interceptor binding, using the JDK algorithm for determining an annotation hash code, + * ignoring any members annotated with {@link Nonbinding}. + * + * @param interceptorBinding the interceptor binding to consider + * @return the hashCode for the interceptor binding + * @since 1.1 + */ + public int getInterceptorBindingHashCode(Annotation interceptorBinding); + + /** + * Obtains an active {@linkplain javax.enterprise.context.spi.Context context object} for the given + * {@linkplain javax.enterprise.context scope} . + * + * @param scopeType the {@linkplain javax.enterprise.context scope} + * @return the {@linkplain javax.enterprise.context.spi.Context context object} + * @throws ContextNotActiveException if there is no active context object for the given scope + * @throws IllegalArgumentException if there is more than one active context object for the given scope + */ + public Context getContext(Class scopeType); + + /** + * Returns a {@link javax.el.ELResolver} that resolves beans by EL name. + * + * @return the {@link javax.el.ELResolver} + */ + public ELResolver getELResolver(); + + /** + * Returns a wrapper {@link javax.el.ExpressionFactory} that delegates {@link javax.el.MethodExpression} and + * {@link javax.el.ValueExpression} creation to the given {@link javax.el.ExpressionFactory}. When a Unified EL expression + * is evaluated using a {@link javax.el.MethodExpression} or {@link javax.el.ValueExpression} returned by the wrapper + * {@link javax.el.ExpressionFactory}, the container handles destruction of objects with scope + * {@link javax.enterprise.context.Dependent}. + * + * + * @param expressionFactory the {@link javax.el.ExpressionFactory} to wrap + * @return the wrapped {@link javax.el.ExpressionFactory} + */ + public ExpressionFactory wrapExpressionFactory(ExpressionFactory expressionFactory); + + /** + * Obtain an {@link AnnotatedType} that may be used to read the annotations of the given class or interface. + * + * @param the class or interface + * @param type the {@link java.lang.Class} object + * @return the {@link AnnotatedType} + */ + public AnnotatedType createAnnotatedType(Class type); + + /** + *

+ * Obtains an {@link InjectionTarget} for the given {@link AnnotatedType}. The container ignores the annotations and types + * declared by the elements of the actual Java class and uses the metadata provided via the {@link Annotated} interface + * instead. + *

+ * + *

+ * This method is deprecated from CDI 1.1 and {@link #getInjectionTargetFactory(AnnotatedType)} should be used instead. + *

+ * + * @param the type + * @param type the {@link AnnotatedType} + * @return a container provided implementation of {@link InjectionTarget} + * @throws IllegalArgumentException if there is a definition error associated with any injection point of the type + */ + public InjectionTarget createInjectionTarget(AnnotatedType type); + + /** + *

+ * An implementation of {@link InjectionTargetFactory} that provides container created {@link InjectionTarget} instances. + *

+ * + *

+ * This factory can be wrapped to add behavior to container created injection targets. + *

+ * + * @param annotatedType the annotated type to create the injection target factory for + * @return an {@link InjectionTargetFactory} + * @since 1.1 + */ + public InjectionTargetFactory getInjectionTargetFactory(AnnotatedType annotatedType); + + /** + *

+ * An implementation of {@link ProducerFactory} that provides container created {@link Producer} instances for the given + * field. + *

+ * + *

+ * This factory can be wrapped to add behavior to container created producers. + *

+ * + * @param field the field to create the producer factory for + * @param declaringBean the bean declaring the producer. May be null if the producer is static or the declaring object is + * non-contextual + * @return the producer factory for the field + * @since 1.1 + */ + public ProducerFactory getProducerFactory(AnnotatedField field, Bean declaringBean); + + /** + *

+ * An implementation of {@link ProducerFactory} that provides container created {@link Producer} instances for the given + * method. + *

+ * + *

+ * This factory can be wrapped to add behavior to container created producers. + *

+ * + * @param method the method to create the producer factory for + * @param declaringBean the bean declaring the producer. May be null if the producer is static or the declaring object is + * non-contextual + * @return the producer factory for the method + * @since 1.1 + */ + public ProducerFactory getProducerFactory(AnnotatedMethod method, Bean declaringBean); + + /** + * Obtains a {@link BeanAttributes} for the given {@link AnnotatedType}. The container ignores the annotations and types + * declared by the elements of the actual Java class and uses the metadata provided via the {@link Annotated} interface + * instead. + * + * @param the type + * @param type the {@link AnnotatedType} + * @return a container provided implementation of {@link InjectionTarget} + * @since 1.1 + */ + public BeanAttributes createBeanAttributes(AnnotatedType type); + + /** + * Obtains a {@link BeanAttributes} for the given {@link AnnotatedType}. The container ignores the annotations and types + * declared by the elements of the actual Java class and uses the metadata provided via the {@link Annotated} interface + * instead. + * + * @param type the {@link AnnotatedType} + * @return a container provided implementation of {@link InjectionTarget} + * @since 1.1 + */ + public BeanAttributes createBeanAttributes(AnnotatedMember type); + + /** + *

+ * Obtains a {@link Bean} for the given {@link BeanAttributes}, bean class and {@link InjectionTarget}. + *

+ * + *

+ * The {@link InjectionTarget} creates and destroys instances of the bean, performs dependency injection and lifecycle + * callbacks, and determines the return value of {@link Bean#getInjectionPoints()}. The {@link InjectionTarget} is obtained + * from the {@link InjectionTargetFactory}. {@link #getInjectionTargetFactory(AnnotatedType)} allows use of a container created + * {@link InjectionTarget}. + *

+ * + * @param the type + * @param attributes a {@link BeanAttributes} which determines the bean types, qualifiers, scope, name and stereotypes of + * the returned {@link Bean}, and the return values of {@link Bean#isAlternative()} and {@link Bean#isNullable()} + * @param beanClass a class, which determines the return value of {@link Bean#getBeanClass()} + * @param injectionTargetFactory an {@link InjectionTargetFactory}, used to obtain an {@link InjectionTarget} + * @return a container provided implementation of {@link Bean} + * @since 1.1 + */ + public Bean createBean(BeanAttributes attributes, Class beanClass, + InjectionTargetFactory injectionTargetFactory); + + /** + *

+ * Obtains a {@link Bean} for the given {@link BeanAttributes}, bean class and {@link Producer}. + *

+ * + *

+ * The {@link Producer} creates and destroys instances of the decorator, and determines the return value of + * {@link Bean#getInjectionPoints()}. The {@link Producer} is obtained from the {@link ProducerFactory}. + * {@link #getProducerFactory(AnnotatedMethod, Bean)} or {@link #getProducerFactory(AnnotatedField, Bean)} allows use of a + * container created {@link Producer}. + *

+ * + * @param the type + * @param the type of the declaring bean + * @param attributes a {@link BeanAttributes} which determines the bean types, qualifiers, scope, name and stereotypes of + * the returned {@link Bean}, and the return values of {@link Bean#isAlternative()} and {@link Bean#isNullable()} + * @param beanClass a class, which determines the return value of Bean.getClass() + * @param producerFactory a {@link ProducerFactory}, used to obtain a {@link Producer} + * @return a container provided implementation of {@link Bean} + * @since 1.1 + */ + public Bean createBean(BeanAttributes attributes, Class beanClass, ProducerFactory producerFactory); + + /** + * Obtains a container provided implementation of {@link InjectionPoint} for the given {@link AnnotatedField}. + * + * @param field the {@link AnnotatedField} defining the injection point + * @return the container provided {@link InjectionPoint} + * @throws IllegalArgumentException if there is a definition error associated with the injection point + * @since 1.1 + */ + public InjectionPoint createInjectionPoint(AnnotatedField field); + + /** + * Obtains a container provided implementation of {@link InjectionPoint} for the given {@link AnnotatedParameter}. + * + * @param parameter the {@link AnnotatedParameter} defining the injection point + * @return the container provided {@link InjectionPoint} + * @throws IllegalArgumentException if there is a definition error associated with the injection point + * @since 1.1 + */ + public InjectionPoint createInjectionPoint(AnnotatedParameter parameter); + + /** + * Obtains the container's instance of an Extension class declared in META-INF/services. + * + * @param the type of the extension + * @param extensionClass the type of the extension class + * @return the extension instance + * @throws IllegalArgumentException if the container has no instance of the given class + * @since 1.1 + */ + public T getExtension(Class extensionClass); + + /** + * + * Create an {@link InterceptionFactory} for the given {@link CreationalContext} and type. + * + * @param ctx {@link CreationalContext} for the {@link InterceptionFactory} to create + * @param clazz class of the instance this factory will work on + * @param type of the instance this factory will work on + * @return a new {@link InterceptionFactory} to add services on on instances of T + * @since 2.0 + */ + InterceptionFactory createInterceptionFactory(CreationalContext ctx, Class clazz); + + + /** + * + * Returns an instance of Event with specified type java.lang.Object and specified qualifier @Default + * It allows typesafe synchronous or asynchronous event firing without injection of {@link Event} built-in bean requirement. + * + * @return a new {@link Event} object whose event type is Object and qualifier @Default + * @since 2.0 + */ + Event getEvent(); + + + /** + * + * Obtains an {@link Instance} object to access to beans instances. + * + * The returned Instance object can only access instances of beans that are available for injection in the module + * or library containing the class into which the BeanManager was injected or the Java EE component from whose JNDI + * environment namespace the BeanManager was obtained, according to the rules of typesafe resolution. + * + * Note that when called during invocation of an {@link AfterBeanDiscovery} event observer, + * the Instance returned by this method will only give access to instances of beans discovered by the container + * before the {@link AfterBeanDiscovery} event is fired. + * + * Instances of dependent scoped beans obtained with this Instance must be explicitly destroyed by calling {@link Instance#destroy(Object)} + * + * If no qualifier is passed to {@link Instance#select} method, the @Default qualifier is assumed. + * + * @return an {@link Instance} object to request beans instances + * @throws IllegalStateException if called during application initialization, before the {@link AfterDeploymentValidation} + * event is fired. + * @since 2.0 + */ + Instance createInstance(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeforeBeanDiscovery.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeforeBeanDiscovery.java new file mode 100644 index 000000000..d4b3ae5ea --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeforeBeanDiscovery.java @@ -0,0 +1,246 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.context.NormalScope; +import javax.enterprise.inject.Stereotype; +import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator; +import javax.inject.Qualifier; +import javax.inject.Scope; +import javax.interceptor.InterceptorBinding; +import java.lang.annotation.Annotation; + +/** + *

+ * This event type is thrown by the container before the bean discovery process begins. If any observer method of the + * {@code BeforeBeanDiscovery} event throws an exception, the exception is treated as a definition error by the container. + *

+ * + * @author Pete Muir + * @author David Allen + * @author Antoine Sabot-Durand + */ +public interface BeforeBeanDiscovery { + /** + *

+ * Declares an annotation type as a {@linkplain javax.inject.Qualifier} qualifier type. + *

+ * + *

+ * This is only required if you wish to make an annotation a qualifier without adding {@link Qualifier} to it. + *

+ * + * @param qualifier The annotation to treat as a qualifier + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addQualifier(Class qualifier); + + /** + *

+ * Declares an annotation type as a {@linkplain javax.inject.Qualifier} qualifier type. + *

+ * + *

+ * This is only required if you wish to make an annotation a qualifier without adding {@link Qualifier} to it. + *

+ * + * @param qualifier The annotation to treat as a qualifier + * @throws IllegalStateException if called outside of the observer method invocation + * @since 1.1 + */ + public void addQualifier(AnnotatedType qualifier); + + /** + *

+ * Declares an annotation type as a {@linkplain javax.enterprise.context scope type}. + *

+ * + *

+ * This is only required if you wish to make an annotation a scope type without adding the {@link NormalScope} or + * {@link Scope} annotations to it. You can also use this method to override an existing normal scope definition. + *

+ * + * @see AfterBeanDiscovery#addContext(javax.enterprise.context.spi.Context) + * + * @param scopeType The annotation type to treat as a {@linkplain javax.enterprise.context scope type} + * @param normal Indicates if the scope is normal + * @param passivating Indicates if the scope is {@linkplain javax.enterprise.inject.spi.PassivationCapable passivation + * capable} + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addScope(Class scopeType, boolean normal, boolean passivating); + + /** + *

+ * Declares an annotation type as a {@linkplain javax.enterprise.inject.Stereotype stereotype}, and specifies its + * meta-annotations. + *

+ * + *

+ * This is only required if you wish to make an annotation a stereotype without adding {@link Stereotype} to it. You can + * also use this method to override an existing stereotype definition. + *

+ * + * @param stereotype The annotation type to treat as a {@linkplain javax.enterprise.inject.Stereotype stereotype} + * @param stereotypeDef An optional list of annotations defining the {@linkplain javax.enterprise.inject.Stereotype + * stereotype} + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addStereotype(Class stereotype, Annotation... stereotypeDef); + + /** + *

+ * Declares an annotation type as an {@linkplain Interceptor interceptor} binding type. + *

+ * + *

+ * This is only required if you wish to make an annotation an interceptor binding type without adding + * {@link InterceptorBinding} to it. + *

+ * + * @param bindingType The annotation type to treat as an interceptor binding type + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addInterceptorBinding(AnnotatedType bindingType); + + /** + *

+ * Declares an annotation type as an {@linkplain Interceptor interceptor} binding type, and specifies its meta-annotations. + *

+ * + *

+ * This is only required if you wish to make an annotation an interceptor binding type without adding + * {@link InterceptorBinding} to it. + *

+ * + * @param bindingType The annotation type to treat as an interceptor binding type + * @param bindingTypeDef An optional list of annotations defining the {@linkplain Interceptor interceptor} + * @throws IllegalStateException if called outside of the observer method invocation + * @since 1.1 + */ + public void addInterceptorBinding(Class bindingType, Annotation... bindingTypeDef); + + /** + *

+ * Adds a given {@link javax.enterprise.inject.spi.AnnotatedType} to the set of types which will be scanned during bean + * discovery. + *

+ * + *

+ * This method is deprecated from CDI 1.1 and {@link #addAnnotatedType(AnnotatedType, String)} should be used instead. + *

+ * + * @throws IllegalStateException if called outside of the observer method invocation + * @param type The {@link javax.enterprise.inject.spi.AnnotatedType} to add for later scanning + */ + public void addAnnotatedType(AnnotatedType type); + + /** + *

+ * Adds a given {@link javax.enterprise.inject.spi.AnnotatedType} to the set of types which will be scanned during bean + * discovery. + *

+ * + *

+ * Thanks to the id parameter, this method allows multiple annotated types, based on the same underlying type, to be defined. {@link AnnotatedType}s + * discovered by the container use the fully qualified class name of {@link AnnotatedType#getJavaClass()} to identify the + * type. + *

+ * + *

+ * {@link AfterBeanDiscovery#getAnnotatedType(Class, String)} and {@link AfterBeanDiscovery#getAnnotatedTypes(Class)} allows + * annotated types to be obtained by identifier. + *

+ * + * @param type The {@link javax.enterprise.inject.spi.AnnotatedType} to add for later scanning + * @param id the identifier used to distinguish this AnnotatedType from an other one based on the same underlying type + * @throws IllegalStateException if called outside of the observer method invocation + * @since 1.1 + */ + public void addAnnotatedType(AnnotatedType type, String id); + + /** + *

+ * Obtains a new {@link AnnotatedTypeConfigurator} to configure a new {@link javax.enterprise.inject.spi.AnnotatedType} and + * add it to the set of types which will be scanned during bean discovery at the end of the observer invocation + *

+ * + *

+ * Thanks to the id parameter, this method allows multiple annotated types, based on the same underlying type, to be defined with a builder. + * {@link AnnotatedType}s discovered by the container use the fully qualified class name of + * {@link AnnotatedType#getJavaClass()} to identify the type. + *

+ * + *

+ * {@link AfterBeanDiscovery#getAnnotatedType(Class, String)} and {@link AfterBeanDiscovery#getAnnotatedTypes(Class)} allows + * annotated types to be obtained by identifier. + *

+ * + * Each call returns a new AnnotatedTypeConfigurator + * + * + * @param type class used to initialized the type and annotations on the {@link AnnotatedTypeConfigurator} + * @param id the identifier used to distinguish this AnnotatedType from an other one based on the same underlying type + * @return a non reusable {@link AnnotatedTypeConfigurator} to configure the new AnnotatedType + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public AnnotatedTypeConfigurator addAnnotatedType(Class type, String id); + + + /** + * + *

+ * Obtains a new {@link AnnotatedTypeConfigurator} to configure a new {@link javax.enterprise.inject.spi.AnnotatedType} + * and declares it as a {@linkplain javax.inject.Qualifier} qualifier type. + *

+ * + *

+ * This is only required if you wish to make an annotation a qualifier without adding {@link Qualifier} to it and need to + * easily add other annotations (like {@link javax.enterprise.util.Nonbinding} on its members. + *

+ * + * @param qualifier The annotation class used to initialized the configurator + * @throws IllegalStateException if called outside of the observer method invocation + * @return a non reusable {@link AnnotatedTypeConfigurator} to configure the qualifier + * @since 2.0 + */ + AnnotatedTypeConfigurator configureQualifier(Class qualifier); + + + /** + * + *

+ * Obtains a new {@link AnnotatedTypeConfigurator} to configure a new {@link javax.enterprise.inject.spi.AnnotatedType} + * and declares it as an {@linkplain Interceptor interceptor} binding type. + *

+ * + *

+ * This is only required if you wish to make an annotation an interceptor binding type without adding + * {@link InterceptorBinding} to it and need to easily add other annotations + * (like {@link javax.enterprise.util.Nonbinding} on its members. + *

+ * + * @param bindingType The annotation class used to initialized the configurator + * @throws IllegalStateException if called outside of the observer method invocation + * @return a non reusable {@link AnnotatedTypeConfigurator} to configure the interceptor binding + * @since 2.0 + */ + AnnotatedTypeConfigurator configureInterceptorBinding(Class bindingType); + + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeforeShutdown.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeforeShutdown.java new file mode 100644 index 000000000..d055b79fa --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/BeforeShutdown.java @@ -0,0 +1,28 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The type of the final event the container fires after it has finished processing requests and destroyed all contexts. If any + * observer method of the {@code BeforeShutdown} event throws an exception, the exception is ignored by the container. + *

+ * + * @author David Allen + */ +public interface BeforeShutdown { +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/CDI.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/CDI.java new file mode 100644 index 000000000..7ea46387d --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/CDI.java @@ -0,0 +1,141 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, 2015, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.inject.Instance; + +import java.util.Collections; +import java.util.Comparator; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.TreeSet; + +/** + * Provides access to the current container. + * + *

+ * CDI implements {@link Instance} and therefore might be used to perform programmatic lookup. + * If no qualifier is passed to {@link #select} method, the @Default qualifier is assumed. + *

+ * + * + * @author Pete Muir + * @author Antoine Sabot-Durand + * @author John D. Ament + * @since 1.1 + * @param type inherited from {@link Instance}. Always Object for CDI + */ +public abstract class CDI implements Instance { + + private static final Object lock = new Object(); + protected static volatile Set discoveredProviders = null; + protected static volatile CDIProvider configuredProvider = null; + + /** + *

+ * Get the CDI instance that provides access to the current container. + *

+ * + *

+ * If there are no providers available, an {@link IllegalStateException} is thrown, otherwise the first provider which can + * access the container is used. + *

+ * + * @throws IllegalStateException if no {@link CDIProvider} is available + * @return the CDI instance + */ + public static CDI current() { + return getCDIProvider().getCDI(); + } + + /** + * + * Obtain the {@link CDIProvider} the user set with {@link #setCDIProvider(CDIProvider)}, or if it wasn't set, use the + * serviceloader the retrieve the {@link CDIProvider} with the highest priority. + * + * @return the {@link CDIProvider} set by user or retrieved by serviceloader + */ + private static CDIProvider getCDIProvider() { + if (configuredProvider != null) { + return configuredProvider; + } else { + // Discover providers and cache + if (discoveredProviders == null) { + synchronized (lock) { + if (discoveredProviders == null) { + findAllProviders(); + } + } + } + configuredProvider = discoveredProviders.stream() + .filter(c -> c.getCDI() != null) + .findFirst().orElseThrow(() -> new IllegalStateException("Unable to access CDI")); + return configuredProvider; + } + } + + /** + *

+ * Set the {@link CDIProvider} to use. + *

+ * + *

+ * If a {@link CDIProvider} is set using this method, any provider specified as a service provider will not be used. + *

+ * + * @param provider the provider to use + * @throws IllegalStateException if the {@link CDIProvider} is already set + */ + public static void setCDIProvider(CDIProvider provider) { + if (provider != null) { + configuredProvider = provider; + } else { + throw new IllegalStateException("CDIProvider must not be null"); + } + } + + private static void findAllProviders() { + + ServiceLoader providerLoader; + Set providers = new TreeSet<>(Comparator.comparingInt(CDIProvider::getPriority).reversed()); + + providerLoader = SecurityActions.loadService(CDIProvider.class, CDI.class.getClassLoader()); + + if(! providerLoader.iterator().hasNext()) { + throw new IllegalStateException("Unable to locate CDIProvider"); + } + + try { + providerLoader.forEach(providers::add); + } catch (ServiceConfigurationError e) { + throw new IllegalStateException(e); + } + CDI.discoveredProviders = Collections.unmodifiableSet(providers); + } + + // Helper methods + + /** + * Get the CDI BeanManager for the current context + * + * @return the BeanManager + */ + public abstract BeanManager getBeanManager(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/CDIProvider.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/CDIProvider.java new file mode 100644 index 000000000..77da18093 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/CDIProvider.java @@ -0,0 +1,43 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, 2015 Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + * Interface implemented by a CDI provider to provide access to the current container + * + * @author Pete Muir + * @since 1.1 + */ +public interface CDIProvider extends Prioritized { + + public static final int DEFAULT_CDI_PROVIDER_PRIORITY = 0; + + /** + * Provides access to the current container + * + * @return the CDI instance for the current container + * @throws IllegalStateException if no CDI container is available + */ + CDI getCDI(); + + + @Override + default int getPriority() { + return DEFAULT_CDI_PROVIDER_PRIORITY; + }; +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Decorator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Decorator.java new file mode 100644 index 000000000..1ad9bbeb6 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Decorator.java @@ -0,0 +1,68 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Set; + +/** + *

+ * Represents an enabled {@linkplain javax.decorator decorator}. + *

+ *

+ * Since CDI 2.0, an implementation of this interface may implement {@link Prioritized} in order to enable the decorator with + * given priority value for entire application. + *

+ * + * @author Gavin King + * @author Pete Muir + * + * @param the decorator bean class + */ +public interface Decorator extends Bean { + + /** + *

+ * Obtains the {@linkplain Type type} of the {@linkplain javax.decorator.Delegate delegate injection point}. + *

+ * + * @return the delegate {@linkplain Type type} + */ + public Type getDelegateType(); + + /** + *

+ * Obtains the {@linkplain javax.inject.Qualifier qualifiers} of the {@linkplain javax.decorator.Delegate delegate injection + * point}. + *

+ * + * @return the delegate {@linkplain javax.inject.Qualifier qualifiers} + */ + public Set getDelegateQualifiers(); + + /** + *

+ * Obtains the {@linkplain javax.decorator decorated types}. + *

+ * + * @return the set of decorated {@linkplain Type types} + */ + public Set getDecoratedTypes(); + +} \ No newline at end of file diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/DefinitionException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/DefinitionException.java new file mode 100644 index 000000000..501518095 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/DefinitionException.java @@ -0,0 +1,59 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * Thrown when a definition error occurs. + *

+ * + *

+ * Definition errors are developer errors. They may be detected by tooling at development time, and are also detected by the + * container at initialization time. If a definition error exists in a deployment, initialization will be aborted by the + * container. + *

+ * + *

+ * The container is permitted to define a non-portable mode, for use at development time, in which some definition errors do not + * cause application initialization to abort. + *

+ * + *

+ * An implementation is permitted to throw a subclass of {@link DefinitionException} for any definition error which exists. + *

+ * + * @author Pete Muir + * @since 1.1 + */ +public class DefinitionException extends RuntimeException { + + private static final long serialVersionUID = -2699170549782567339L; + + public DefinitionException(String message, Throwable t) { + super(message, t); + } + + public DefinitionException(String message) { + super(message); + } + + public DefinitionException(Throwable t) { + super(t); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/DeploymentException.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/DeploymentException.java new file mode 100644 index 000000000..2c68816df --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/DeploymentException.java @@ -0,0 +1,59 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * Thrown when a deployment problem occurs. + *

+ * + *

+ * Deployment problems are detected by the container at initialization time. If a deployment problem exists in a deployment, + * initialization will be aborted by the container. + *

+ * + *

+ * The container is permitted to define a non-portable mode, for use at development time, in which some deployment problems do + * not cause application initialization to abort. + *

+ * + *

+ * An implementation is permitted to throw a subclass of {@link DeploymentException} for any deployment problem. + *

+ * + * @author Pete Muir + * @since 1.1 + * + */ +public class DeploymentException extends RuntimeException { + + private static final long serialVersionUID = 2604707587772339984L; + + public DeploymentException(String message, Throwable t) { + super(message, t); + } + + public DeploymentException(String message) { + super(message); + } + + public DeploymentException(Throwable t) { + super(t); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/EventContext.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/EventContext.java new file mode 100644 index 000000000..4889d5503 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/EventContext.java @@ -0,0 +1,43 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016 Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + * Represents a context of a fired event. Provides access to an event object and corresponding metadata. + * + * @author Martin Kouba + * @see ObserverMethod#notify(EventContext) + * @see EventMetadata + * @since 2.0 + * @param type of event object + */ +public interface EventContext { + + /** + * + * @return the event object, aka the payload + */ + T getEvent(); + + /** + * + * @return the event metadata + */ + EventMetadata getMetadata(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/EventMetadata.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/EventMetadata.java new file mode 100644 index 000000000..c53debdab --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/EventMetadata.java @@ -0,0 +1,69 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, 2015 Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Set; + +import javax.enterprise.event.Event; +import javax.enterprise.event.Observes; + +/** + *

+ * Provides access to metadata about an observed event payload. The metadata may be for events fired with either of + * {@link javax.enterprise.event.Event} or {@link BeanManager#fireEvent(Object, Annotation...)}. + *

+ *

+ * {@link EventMetadata} may only be injected into an observer method. For example: + *

+ * + *
+ * public void afterLogin(@Observes LoggedInEvent event, EventMetadata eventMetadata) { ... }
+ * 
+ * + * @see Observes + * + * @author Lincoln Baxter, III + * @author Pete Muir + * @author Antoine Sabot-Durand + * @since 1.1 + */ +public interface EventMetadata { + /** + * @return the qualifiers for which event payload was fired. + */ + public Set getQualifiers(); + + /** + * Get the {@link InjectionPoint} representing the injected {@link Event} instance which fired the event, or + * null if it was fired from {@link BeanManager#fireEvent(Object, Annotation...)}; + * + * @return InjectionPoint of the Event + */ + public InjectionPoint getInjectionPoint(); + + /** + * Get the type representing runtime class of the event object with type variables resolved. + * + * + * @return the runtime type of the event object + */ + public Type getType(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Extension.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Extension.java new file mode 100644 index 000000000..e9d010e5c --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Extension.java @@ -0,0 +1,47 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * Service interface implemented by extensions. An extension is a service provider declared in META-INF/services. + *

+ * + *

+ * Service providers may have {@linkplain javax.enterprise.event.Observes observer methods}, which may observe any event, + * including any {@linkplain javax.enterprise.inject.spi container lifecycle event}, and obtain an injected + * {@link javax.enterprise.inject.spi.BeanManager}. + *

+ * + *

+ * The container instantiates a single instance of each extension at the beginning of the application initialization process and + * maintains a reference to it until the application shuts down. The container delivers event notifications to this instance by + * calling its observer methods. + *

+ * + *

+ * Service providers are made available for injection as beans with the qualifier {@link javax.enterprise.inject.Default + * @Default}. + *

+ * + * @author Gavin King + * @author Pete Muir + * + */ +public interface Extension { +} \ No newline at end of file diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InjectionPoint.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InjectionPoint.java new file mode 100644 index 000000000..cd039e38f --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InjectionPoint.java @@ -0,0 +1,123 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Member; +import java.lang.reflect.Type; +import java.util.Set; + +import javax.enterprise.inject.Instance; + +/** + *

+ * Provides access to metadata about an injection point. May represent an {@linkplain javax.inject.Inject injected field} or a + * parameter of a {@linkplain javax.inject.Inject bean constructor}, {@linkplain javax.inject.Inject initializer method}, + * {@linkplain javax.enterprise.inject.Produces producer method}, {@linkplain javax.enterprise.inject.Disposes disposer method} + * or {@linkplain javax.enterprise.event.Observes observer method}. + *

+ * + *

+ * If the injection point is a dynamically selected reference obtained then the metadata obtain reflects the injection point of + * the {@link Instance}, with the required type and any additional required qualifiers defined by {@linkplain Instance + * Instance.select()}. + *

+ * + *

+ * Occasionally, a bean with scope {@link javax.enterprise.context.Dependent @Dependent} needs to access metadata relating + * to the object to which it belongs. The bean may inject an {@code InjectionPoint} representing the injection point into which + * the bean was injected. + *

+ * + *

+ * For example, the following producer method creates injectable Logger s. The log category of a Logger + * depends upon the class of the object into which it is injected. + *

+ * + *
+ * @Produces
+ * Logger createLogger(InjectionPoint injectionPoint) {
+ *     return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
+ * }
+ * 
+ * + *

+ * Only {@linkplain javax.enterprise.context.Dependent dependent} objects, may obtain information about the injection point to + * which they belong. + *

+ * + * @author Gavin King + * @author Pete Muir + */ +public interface InjectionPoint { + + /** + * Get the required type of injection point. + * + * @return the required type + */ + public Type getType(); + + /** + * Get the required qualifiers of the injection point. + * + * @return the required qualifiers + */ + public Set getQualifiers(); + + /** + * Get the {@link javax.enterprise.inject.spi.Bean} object representing the bean that defines the injection point. If the + * injection point does not belong to a bean, return a null value. + * + * @return the {@link javax.enterprise.inject.spi.Bean} object representing bean that defines the injection point, of null + * if the injection point does not belong to a bean + */ + public Bean getBean(); + + /** + * Get the {@link java.lang.reflect.Field} object in the case of field injection, the {@link java.lang.reflect.Method} + * object in the case of method parameter injection or the {@link java.lang.reflect.Constructor} object in the case of + * constructor parameter injection. + * + * @return the member + */ + public Member getMember(); + + /** + * Obtain an instance of {@link javax.enterprise.inject.spi.AnnotatedField} or + * {@link javax.enterprise.inject.spi.AnnotatedParameter}, depending upon whether the injection point is an injected field + * or a constructor/method parameter. + * + * @return an {@code AnnotatedField} or {@code AnnotatedParameter} + */ + public Annotated getAnnotated(); + + /** + * Determines if the injection point is a decorator delegate injection point. + * + * @return true if the injection point is a decorator delegate injection point, and false otherwise + */ + public boolean isDelegate(); + + /** + * Determines if the injection is a transient field. + * + * @return true if the injection point is a transient field, and false otherwise + */ + public boolean isTransient(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InjectionTarget.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InjectionTarget.java new file mode 100644 index 000000000..1780ffd19 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InjectionTarget.java @@ -0,0 +1,67 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.context.spi.CreationalContext; + +/** + *

+ * Provides operations for performing {@linkplain javax.enterprise.inject dependency injection} and lifecycle callbacks on an + * instance of a type. + *

+ * + * @see javax.annotation.PostConstruct + * @see javax.annotation.PreDestroy + * + * @author Pete Muir + * @author David Allen + * @param The class of the instance + */ +public interface InjectionTarget extends Producer { + + /** + *

+ * Performs dependency injection upon the given object. Performs Java EE component environment injection, sets the value of + * all injected fields, and calls all initializer methods. + *

+ * + * @param instance The instance upon which to perform injection + * @param ctx The {@link javax.enterprise.context.spi.CreationalContext} to use for creating new instances + */ + public void inject(T instance, CreationalContext ctx); + + /** + *

+ * Calls the {@link javax.annotation.PostConstruct} callback, if it exists, according to the semantics required by the Java + * EE platform specification. + *

+ * + * @param instance The instance on which to invoke the {@link javax.annotation.PostConstruct} method + */ + public void postConstruct(T instance); + + /** + *

+ * Calls the {@link javax.annotation.PreDestroy} callback, if it exists, according to the semantics required by the Java EE + * platform specification. + *

+ * + * @param instance The instance on which to invoke the {@link javax.annotation.PreDestroy} method + */ + public void preDestroy(T instance); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InjectionTargetFactory.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InjectionTargetFactory.java new file mode 100644 index 000000000..5812809bb --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InjectionTargetFactory.java @@ -0,0 +1,77 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator; + +/** + *

+ * An {@link InjectionTargetFactory} can create an {@link InjectionTarget} for a given bean. + *

+ * + *

+ * The {@link InjectionTargetFactory} obtained from {@link BeanManager#getInjectionTargetFactory(AnnotatedType)} is capable of providing + * container created injection targets. This factory can be wrapped to add behavior to container created injection targets. + *

+ * + *

+ * For example: + *

+ * + *
+ * BeanAttributes<MyBean> myBeanAttributes = beanManager.createBeanAttributes(myBeanAnnotatedType);
+ * beanManager.createBean(myBeanAttributes, MyBean.class, new InjectionTargetFactory() {
+ * 
+ *     public <T> InjectionTarget<T> createInjectionTarget(Bean<T> bean) {
+ *         return new WrappingInjectionTarget<T>(beanManager.getInjectionTargetFactory(myBeanAnnotatedType).createInjectionTarget(
+ *                 bean));
+ *     }
+ * });
+ * 
+ * + * @author Pete Muir + * @author Antoine Sabot-Durand + * @since 1.1 + * + * @param type on which this InjectionTarget operates + */ +public interface InjectionTargetFactory { + + /** + * Create a new injection target for a bean. + * + * @param bean the bean to create the injection target for, or null if creating a non-contextual object + * @return the injection target + */ + public InjectionTarget createInjectionTarget(Bean bean); + + /** + * + * Returns an {@link AnnotatedTypeConfigurator} to to configure the {@link AnnotatedType} used to create the {@link InjectionTarget}. + * + * Each call returns the same AnnotatedTypeConfigurator. + * + * @return an {@link AnnotatedTypeConfigurator} to configure injection points + * @throws IllegalStateException if used after {@link #createInjectionTarget(Bean)} invocation + * @since 2.0 + */ + public default AnnotatedTypeConfigurator configure() { + throw new UnsupportedOperationException("Configuration not supported here"); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InterceptionFactory.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InterceptionFactory.java new file mode 100644 index 000000000..c3e66f026 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InterceptionFactory.java @@ -0,0 +1,137 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.context.Dependent; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator; + +/** + * {@link InterceptionFactory} allows to create a wrapper instance whose method invocations are intercepted by method + * interceptors and forwarded to a provided instance. + * + *

+ * An implementation of {@link InterceptionFactory} may be obtained by calling + * {@link BeanManager#createInterceptionFactory(CreationalContext, Class)} to be used in the create method of a custom bean for + * example. + *

+ * + *
+ * public class MyCustomBean implements Bean<MyClass> {
+ *
+ *     BeanManager bm;
+ *
+ *     public MyBean(BeanManager bm) {
+ *        this.bm = bm;
+ *     }
+ *
+ *     public MyClass create(CreationalContext<MyClass> creationalContext) {
+ *
+ *         InterceptionFactory factory = bm.createInterceptionFactory(creationalContext, MyClass.class);
+ *
+ *         factory.configure().filterMethods(m -> m.getJavaMember().getName().equals("shouldBeTransactional")).findFirst()
+ *                 .ifPresent(m -> m.add(new AnnotationLiteral<Transactional>() {
+ *                 }));
+ *
+ *         return factory.createInterceptedInstance(new MyClass());
+ *     }
+ * }
+ * 
+ * + *

+ * The container must also provide a built-in bean with scope {@link Dependent}, bean type {@link InterceptionFactory} and + * qualifier {@link Default} that can be injected in a producer method parameter. + *

+ * + *
+ * @Produces
+ * @RequestScoped
+ * public MyClass produceMyClass(InterceptionFactory factory) {
+ *     factory.configure().add(new AnnotationLiteral() {
+ *     });
+ *     return factory.createInterceptedInstance(new MyClass());
+ * }
+ * 
+ * + *

+ * Instances of this class are neither reusable nor suitable for sharing between threads. + *

+ * + * @author Antoine Sabot-Durand + * @since 2.0 + * @param type for which the wrapper is created + */ +public interface InterceptionFactory { + + /** + * Instructs the container to ignore all non-static, final methods with public, protected or default visibility declared by + * any class in the type hierarchy of the intercepted instance during invocation of + * {@link #createInterceptedInstance(Object)}. + * + *

+ * Ignored methods should never be invoked upon the wrapper instance created by + * {@link #createInterceptedInstance(Object)}. Otherwise, unpredictable behavior results. + *

+ * + * @return self + */ + InterceptionFactory ignoreFinalMethods(); + + /** + * Returns an {@link AnnotatedTypeConfigurator} initialized with the {@link AnnotatedType} created either for the class + * passed to {@link BeanManager#createInterceptionFactory(CreationalContext, Class)} or derived from the + * {@link InterceptionFactory} parameter injection point. + * + *

+ * The configurator allows to add or remove interceptor bindings used to associate interceptors with the wrapper instance + * returned by {@link #createInterceptedInstance(Object)}. + *

+ * + *

+ * Each call returns the same {@link AnnotatedTypeConfigurator}. + *

+ * + * @return an {@link AnnotatedTypeConfigurator} to configure interceptors bindings + */ + AnnotatedTypeConfigurator configure(); + + /** + * Returns a wrapper instance whose method invocations are intercepted by method interceptors and forwarded to a provided + * instance. + * + *

+ * This method should only be called once, subsequent calls will throw an {@link IllegalStateException}. + *

+ * + *

+ * If T is not proxyable as defined in section 3.11 of the spec an + * {@link javax.enterprise.inject.UnproxyableResolutionException} exception is thrown. Calling {@link #ignoreFinalMethods()} + * before this method can loosen these restrictions. + *

+ * + *

+ * If the provided instance is an internal container construct (such as client proxy), non-portable behavior results. + *

+ * + * @param instance The provided instance + * @return a wrapper instance + */ + T createInterceptedInstance(T instance); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InterceptionType.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InterceptionType.java new file mode 100644 index 000000000..b8a1a9932 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/InterceptionType.java @@ -0,0 +1,65 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * Identifies the kind of lifecycle callback, EJB timeout method or business method interception. + *

+ * + * @author Gavin King + * @author Pete Muir + * + */ +public enum InterceptionType { + + /** + * Intercepts method invocation + */ + AROUND_INVOKE, + + /** + * Intercepts a timeout + */ + AROUND_TIMEOUT, + + /** + * Intercepts a constructor invocation + */ + AROUND_CONSTRUCT, + + /** + * Intercepts bean construction + */ + POST_CONSTRUCT, + + /** + * Intercepts bean destruction + */ + PRE_DESTROY, + + /** + * Intercepts bean passivation, only called for EJBs + */ + PRE_PASSIVATE, + + /** + * Intercepts bean activation, only called for EJBs + */ + POST_ACTIVATE +} \ No newline at end of file diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Interceptor.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Interceptor.java new file mode 100644 index 000000000..ad198ee4a --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Interceptor.java @@ -0,0 +1,77 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.util.Set; + +import javax.interceptor.InvocationContext; + +/** + *

+ * Represents an enabled {@linkplain javax.interceptor interceptor}. + *

+ *

+ * Since CDI 2.0, an implementation of this interface may implement {@link Prioritized} in order to enable the interceptor with + * given priority value for entire application. + *

+ * + * @author Gavin King + * @author Pete Muir + * @author David Allen + * + * @param the interceptor bean class + */ +public interface Interceptor extends Bean { + + /** + *

+ * Obtains the {@linkplain javax.interceptor.InterceptorBinding interceptor bindings} of the interceptor. + *

+ * + * @return the set of {@linkplain javax.interceptor.InterceptorBinding interceptor bindings} + */ + public Set getInterceptorBindings(); + + /** + *

+ * Determines if the interceptor intercepts the specified {@linkplain InterceptionType kind of lifecycle callback or method + * invocation}. + *

+ * + * @param type the {@linkplain InterceptionType kind of interception} + * @return returns true if the interceptor intercepts callbacks or business methods of the given type, and + * false otherwise. + */ + public boolean intercepts(InterceptionType type); + + /** + *

+ * Invokes the specified {@linkplain InterceptionType kind of lifecycle callback or method invocation interception} upon the + * given interceptor instance. + *

+ * + * @param type the {@linkplain InterceptionType kind of interception} + * @param instance the interceptor instance to invoke + * @param ctx the context for the invocation + * @return the invocation return value + * @throws Exception thrown by the target method and/or the following interceptors in the chain + */ + public Object intercept(InterceptionType type, T instance, InvocationContext ctx) throws Exception; + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ObserverMethod.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ObserverMethod.java new file mode 100644 index 000000000..da8ba614c --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ObserverMethod.java @@ -0,0 +1,140 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, 2015 Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Set; + +import javax.enterprise.event.ObservesAsync; +import javax.enterprise.event.Reception; +import javax.enterprise.event.TransactionPhase; + +/** + *

+ * Represents an {@linkplain javax.enterprise.event.Observes observer method} of an {@linkplain javax.enterprise.inject enabled + * bean}. Defines everything the container needs to know about an observer method. + *

+ * + *

+ * If a custom implementation of this interface does not override either {@link #notify(Object)} or + * {@link #notify(EventContext)}, the container automatically detects the problem and treats it as a definition error. + *

+ * + * @author Gavin King + * @author David Allen + * @author Mark Paluch + * @author Antoine Sabot-Durand + * @param the event type + */ +public interface ObserverMethod extends Prioritized { + + public static final int DEFAULT_PRIORITY = javax.interceptor.Interceptor.Priority.APPLICATION + 500; + + /** + *

+ * Obtains the {@linkplain Class class} of the type that declares the observer method. + *

+ * + * @return the defining {@linkplain Class class} + */ + public Class getBeanClass(); + + /** + * Obtains the {@linkplain javax.enterprise.event observed event type}. + * + * @return the observed event {@linkplain Type type} + */ + public Type getObservedType(); + + /** + * Obtains the set of {@linkplain javax.enterprise.event observed event qualifiers}. + * + * @return the observed event {@linkplain javax.inject.Qualifier qualifiers} + */ + public Set getObservedQualifiers(); + + /** + * Obtains the specified {@link Reception} for the observer method. This indicates if the observer is conditional or not. + * + * @return the {@link Reception} + */ + public Reception getReception(); + + /** + * Obtains the specified {@link TransactionPhase} for the observer method. + * + * @return the {@link TransactionPhase} + */ + public TransactionPhase getTransactionPhase(); + + /** + * The priority that will be used by the container to determine the notification order in which event observer + * methods are invoked. + * + * @return The priority that will be used by the container to determine the notification order in which event + * observer methods are invoked. + * @since 2.0 + */ + @Override + public default int getPriority() { + return DEFAULT_PRIORITY; + } + + /** + *

+ * Calls the observer method, passing the given event object. + *

+ * + *

+ * The implementation of this method for a custom observer method is responsible for deciding whether to call + * the method if the {@link #getReception()} returns {@link Reception#IF_EXISTS}. + *

+ * + * @param event the event object + */ + public default void notify(T event) { + } + + /** + * Calls the observer method, passing the given event context. + *

+ * The container should always call this method, but the default implementation delegates to {@link #notify(Object)}. + *

+ * The implementation of this method for a custom observer method is responsible for deciding whether to call the method if + * the {@link #getReception()} returns {@link Reception#IF_EXISTS}. + * + * @param eventContext {@link EventContext} used to notify observers + */ + public default void notify(EventContext eventContext) { + notify(eventContext.getEvent()); + } + + /** + *

+ * Determines if this observer method is asynchronous + *

+ * + * @return returns true if the method is an asynchronous observer method (i.e. defined with {@link ObservesAsync}), + * otherwise returns false + * + */ + public default boolean isAsync() { + return false; + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/PassivationCapable.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/PassivationCapable.java new file mode 100644 index 000000000..530ec3d03 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/PassivationCapable.java @@ -0,0 +1,38 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + * Indicates that a custom implementation of {@link javax.enterprise.inject.spi.Bean} or + * {@link javax.enterprise.context.spi.Contextual} is passivation capable. + * + * @author Gavin King + * @author David Allen + * + */ +public interface PassivationCapable { + /** + * A string that uniquely identifies the instance of {@link javax.enterprise.inject.spi.Bean} or + * {@link javax.enterprise.context.spi.Contextual}. It is recommended that the string contain the package name of the class + * that implements {@code Bean} or {@code Contextual}. + * + * @return a unique identifier for the {@link javax.enterprise.inject.spi.Bean} or + * {@link javax.enterprise.context.spi.Contextual} + */ + public String getId(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Prioritized.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Prioritized.java new file mode 100644 index 000000000..06a520704 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Prioritized.java @@ -0,0 +1,48 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2015, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * This interface allows some SPI implementation to change their priority programmatically. + *

+ * + *

+ * For instance {@link ObserverMethod} extends this interface to set the observer priority. + * + * A custom alternative {@link Bean}, {@link Interceptor} or {@link Decorator} may implement this interface to be activated + * with a given priority + * + *

+ * + * @see Bean + * @author Mark Paluch + * @author Antoine Sabot-Durand + * @since 2.0 + */ +public interface Prioritized { + + /** + *

+ * Returns the priority for this SPI element. + *

+ * + * @return the priority value + */ + int getPriority(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessAnnotatedType.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessAnnotatedType.java new file mode 100644 index 000000000..5cff7a366 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessAnnotatedType.java @@ -0,0 +1,86 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator; + +/** + *

+ * The container fires an event of this type for each Java class or interface it discovers in a bean archive, before it reads + * the declared annotations. + *

+ *

+ * Any observer of this event is permitted to wrap and/or replace the {@link javax.enterprise.inject.spi.AnnotatedType} by calling either {@link #setAnnotatedType(AnnotatedType)} or {@link #configureAnnotatedType()}. + * If both methods are called within an observer notification an {@link IllegalStateException} is thrown. + * The container must use the final value of this property, after all observers have been called, to discover the types and read the annotations of the program elements. + *

+ *

+ * For example, the following observer decorates the {@link javax.enterprise.inject.spi.AnnotatedType} for every class that is + * discovered by the container. + *

+ * + *
+ * public <T> void decorateAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
+ *     pat.setAnnotatedType(decorate(pat.getAnnotatedType()));
+ * }
+ * 
+ *

+ * If any observer method of a {@code ProcessAnnotatedType} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + * @author David Allen + * @author Antoine Sabot-Durand + * @see AnnotatedType + * @param The class being annotated + */ +public interface ProcessAnnotatedType { + /** + * Returns the {@link javax.enterprise.inject.spi.AnnotatedType} object that will be used by the container to read the + * declared annotations. + * + * @return the {@code AnnotatedType} object + * @throws IllegalStateException if called outside of the observer method invocation + */ + public AnnotatedType getAnnotatedType(); + + /** + * Replaces the {@link javax.enterprise.inject.spi.AnnotatedType}. + * + * @param type the new {@link javax.enterprise.inject.spi.AnnotatedType} object to use + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void setAnnotatedType(AnnotatedType type); + + /** + * Returns an {@link AnnotatedTypeConfigurator} initialized with the {@link AnnotatedType} processed by this event + * to configure a new AnnotatedType that will replace the original one at the end of the observer invocation. + * + * Each call returns the same AnnotatedTypeConfigurator. + * + * @return a non reusable {@link AnnotatedTypeConfigurator} to configure the replacing AnnotatedType + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public AnnotatedTypeConfigurator configureAnnotatedType(); + + /** + * Forces the container to ignore this type. + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void veto(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessBean.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessBean.java new file mode 100644 index 000000000..5c4aaefc2 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessBean.java @@ -0,0 +1,84 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The container fires an event of this type for each enabled bean, interceptor or decorator deployed in a bean archive, before + * registering the {@link javax.enterprise.inject.spi.Bean} object. + *

+ *

+ * The event object type depends upon what kind of bean was discovered: + *

+ *
    + *
  • For a managed bean with bean class X, the container must raise an event of type + * {@link javax.enterprise.inject.spi.ProcessManagedBean}.
  • + *
  • For a session bean with bean class X, the container must raise an event of type + * {@link javax.enterprise.inject.spi.ProcessSessionBean}.
  • + *
  • For a producer method with method return type X of a bean with bean class T, the container must raise an event of type + * {@link javax.enterprise.inject.spi.ProcessProducerMethod}.
  • + *
  • For a producer field with field type X of a bean with bean class T, the container must raise an event of type + * {@link javax.enterprise.inject.spi.ProcessProducerField}.
  • + *
  • For a custom implementation of {@link Bean}, the container must raise an event of type {@link ProcessSyntheticBean}.
  • + *
+ *

+ * Resources are considered to be producer fields. + *

+ *

+ * If any observer method of a {@code ProcessBean} event throws an exception, the exception is treated as a definition error by + * the container. + *

+ * + * @see Bean + * @author David Allen + * @param The class of the bean + */ +public interface ProcessBean { + + /** + * Returns the {@link javax.enterprise.inject.spi.AnnotatedType} representing the bean class, the + * {@link javax.enterprise.inject.spi.AnnotatedMethod} representing the producer method, or the + * {@link javax.enterprise.inject.spi.AnnotatedField} representing the producer field. + * + *

+ * If invoked upon a {@link ProcessSyntheticBean} event, non-portable behavior results and the returned value should be ignored. + *

+ * + * @return the {@link javax.enterprise.inject.spi.Annotated} for the bean being registered + * @throws IllegalStateException if called outside of the observer method invocation + */ + public Annotated getAnnotated(); + + /** + * Returns the {@link javax.enterprise.inject.spi.Bean} object that is about to be registered. The + * {@link javax.enterprise.inject.spi.Bean} may implement {@link javax.enterprise.inject.spi.Interceptor} or + * {@link javax.decorator.Decorator}. + * + * @return the {@link javax.enterprise.inject.spi.Bean} object about to be registered + * @throws IllegalStateException if called outside of the observer method invocation + */ + public Bean getBean(); + + /** + * Registers a definition error with the container, causing the container to abort deployment after bean discovery is + * complete. + * + * @param t The definition error to register as a {@link java.lang.Throwable} + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addDefinitionError(Throwable t); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessBeanAttributes.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessBeanAttributes.java new file mode 100644 index 000000000..f12a4c53a --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessBeanAttributes.java @@ -0,0 +1,108 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.inject.New; +import javax.enterprise.inject.spi.configurator.BeanAttributesConfigurator; + +/** + *

+ * The container fires an event of this type for each enabled bean, interceptor or decorator deployed in a bean archive before + * registering the {@link javax.enterprise.inject.spi.Bean} object. + *

+ *

+ * No event is fired for {@link New} qualified beans. + *

+ *

+ * Any observer of this event is permitted to wrap and/or replace the {@link BeanAttributes} by calling either {@link #setBeanAttributes(BeanAttributes)} or {@link #configureBeanAttributes()}. + * If both methods are called within an observer notification an {@link IllegalStateException} is thrown. + * The container must use the final value of this property, after all observers have been called, to manage instances of the bean. + *

+ *

+ * If any observer method of a {@code ProcessBeanAttributes} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + * @author Pete Muir + * @author Antoine Sabot-Durand + * @param The class of the bean + * @since 1.1 + */ +public interface ProcessBeanAttributes { + + /** + * @return the {@link AnnotatedType} representing the managed bean class or session bean class, the {@link AnnotatedMethod} + * representing the producer field, or the {@link AnnotatedField} representing the producer field + * @throws IllegalStateException if called outside of the observer method invocation + */ + public Annotated getAnnotated(); + + /** + * @return the {@link BeanAttributes} object that will be used by the container to manage instances of the bean + * @throws IllegalStateException if called outside of the observer method invocation + */ + public BeanAttributes getBeanAttributes(); + + /** + * Replaces the {@link BeanAttributes}. + * + * @param beanAttributes the new {@link BeanAttributes} to use + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void setBeanAttributes(BeanAttributes beanAttributes); + + /** + * returns a {@link BeanAttributesConfigurator} initialized with the {@link BeanAttributes} processed by this event + * to configure a new BeanAttributes that will replace the original one at the end of the observer invocation. + * + * Each call returns the same BeanAttributesConfigurator. + * + * @return a non reusable {@link BeanAttributesConfigurator} to configure the replacing BeanAttributes + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public BeanAttributesConfigurator configureBeanAttributes(); + + /** + * Registers a definition error with the container, causing the container to abort deployment after bean discovery is + * complete. + * + * @param t the error to add + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addDefinitionError(Throwable t); + + /** + * Forces the container to ignore the bean. + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void veto(); + + + /** + *

Instructs the container to ignore all non-static, final methods with public, protected or default visibility + * declared on any bean type of the specific bean during validation of injection points that require proxyable bean type.

+ * + *

These method should never be invoked upon bean instances. Otherwise, unpredictable behavior results.

+ * + * + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public void ignoreFinalMethods(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessInjectionPoint.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessInjectionPoint.java new file mode 100644 index 000000000..4542287b2 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessInjectionPoint.java @@ -0,0 +1,82 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.inject.spi.configurator.InjectionPointConfigurator; + +/** + *

+ * The container fires an event of this type for every injection point of every Java EE component class supporting injection + * that may be instantiated by the container at runtime, including every managed bean declared using + * {@code javax.annotation.ManagedBean}, EJB session or message-driven bean, enabled bean, enabled interceptor or enabled + * decorator. + *

+ *

+ * Any observer of this event is permitted to wrap and/or replace the {@link javax.enterprise.inject.spi.InjectionPoint} by calling either {@link #setInjectionPoint(InjectionPoint)} or {@link #configureInjectionPoint()}. + * If both methods are called within an observer notification an {@link IllegalStateException} is thrown. + * The container must use the final value of this property, after all observers have been called, he container must use the final + * value of this property, after all observers have been called, whenever it performs injection upon the injection point. + *

+ *

+ * If any observer method of a {@code ProcessInjectionPoint} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + * @see InjectionPoint + * @author Pete Muir + * @author Antoine Sabot-Durand + * @param the declared type of the injection point. + * @param the bean class of the bean that declares the injection point + */ +public interface ProcessInjectionPoint { + + /** + * @return the InjectionPoint object that will be used by the container to perform injection + * @throws IllegalStateException if called outside of the observer method invocation + */ + public InjectionPoint getInjectionPoint(); + + /** + * Replaces the InjectionPoint. + * + * @param injectionPoint the new injection point + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void setInjectionPoint(InjectionPoint injectionPoint); + + /** + * Returns an {@link InjectionPointConfigurator} initialized with the {@link InjectionPoint} processed by this event + * to configure a new InjectionPoint that will replace the original one at the end of the observer invocation. + * + * Each call returns the same InjectionPointConfigurator + * + * @return a non reusable {@link InjectionPointConfigurator} to configure the replacing InjectionPoint + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public InjectionPointConfigurator configureInjectionPoint(); + + /** + * Registers a definition error with the container, causing the container to abort deployment after bean discovery is + * complete. + * + * @param t the definition error + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addDefinitionError(Throwable t); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessInjectionTarget.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessInjectionTarget.java new file mode 100644 index 000000000..21e07e6ee --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessInjectionTarget.java @@ -0,0 +1,83 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The container fires an event of this type for every Java EE component class supporting injection that may be instantiated by + * the container at runtime, including every managed bean declared using {@code javax.annotation.ManagedBean}, EJB session or + * message-driven bean, enabled bean, enabled interceptor or enabled decorator. + *

+ *

+ * Any observer of this event is permitted to wrap and/or replace the {@link javax.enterprise.inject.spi.InjectionTarget}. The + * container must use the final value of this property, after all observers have been called, whenever it performs injection + * upon the managed bean, session bean or other Java EE component class supporting injection. + *

+ *

+ * For example, this observer decorates the {@code InjectionTarget} for all servlets. + *

+ * + *
+ * public <T extends Servlet> void decorateServlet(@Observes ProcessInjectionTarget<T> pit) {
+ *     pit.setInjectionTarget(decorate(pit.getInjectionTarget()));
+ * }
+ * 
+ *

+ * If any observer method of a {@code ProcessInjectionTarget} event throws an exception, the exception is treated as a + * definition error by the container. + *

+ * + * @see InjectionTarget + * @author David Allen + * @param The managed bean class, session bean class or Java EE component class supporting injection + */ +public interface ProcessInjectionTarget { + /** + * Returns the {@link javax.enterprise.inject.spi.AnnotatedType} representing the managed bean class, session bean class or + * other Java EE component class supporting injection. + * + * @return the {@link javax.enterprise.inject.spi.AnnotatedType} of the bean with an injection target + * @throws IllegalStateException if called outside of the observer method invocation + */ + public AnnotatedType getAnnotatedType(); + + /** + * Returns the {@link javax.enterprise.inject.spi.InjectionTarget} object that will be used by the container to perform + * injection. + * + * @return the {@link javax.enterprise.inject.spi.InjectionTarget} object which performs the injection + * @throws IllegalStateException if called outside of the observer method invocation + */ + public InjectionTarget getInjectionTarget(); + + /** + * Replaces the {@link javax.enterprise.inject.spi.InjectionTarget} which performs injection for this target. + * + * @param injectionTarget The new {@link javax.enterprise.inject.spi.InjectionTarget} to use + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void setInjectionTarget(InjectionTarget injectionTarget); + + /** + * Registers a definition error with the container, causing the container to abort deployment after bean discovery is + * complete. + * + * @param t A {@link java.lang.Throwable} representing the definition error + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addDefinitionError(Throwable t); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessManagedBean.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessManagedBean.java new file mode 100644 index 000000000..7925e64a3 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessManagedBean.java @@ -0,0 +1,40 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The container fires an event of this type for each enabled managed bean, before registering the + * {@link javax.enterprise.inject.spi.Bean} object. + *

+ *

+ * If any observer method of a {@code ProcessManagedBean} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + * @author David Allen + * @param The class of the bean + */ +public interface ProcessManagedBean extends ProcessBean { + /** + * Returns the {@link javax.enterprise.inject.spi.AnnotatedType} representing the bean class. + * + * @return the {@link javax.enterprise.inject.spi.AnnotatedType} for the bean being registered + * @throws IllegalStateException if called outside of the observer method invocation + */ + public AnnotatedType getAnnotatedBeanClass(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessObserverMethod.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessObserverMethod.java new file mode 100644 index 000000000..18c2720bd --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessObserverMethod.java @@ -0,0 +1,110 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, 2015, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.inject.spi.configurator.ObserverMethodConfigurator; + +/** + *

+ * The container fires an event of this type for each {@linkplain javax.enterprise.event.Observes observer method} of each + * enabled bean, before registering the {@link javax.enterprise.inject.spi.ObserverMethod} object. + *

+ *

+ * For a custom implementation of {@link ObserverMethod}, the container must raise an event of type {@link ProcessSyntheticObserverMethod}. + *

+ *

+ * Any observer of this event is permitted to wrap and/or replace the {@link javax.enterprise.inject.spi.ObserverMethod} by calling either {@link #setObserverMethod(ObserverMethod)} or {@link #configureObserverMethod()}. + * If both methods are called within an observer notification an {@link IllegalStateException} is thrown. + * The container must use the final value of this property, after all observers have been called, he container must use the final + * value of this property, after all observers have been called, whenever it performs observer resolution. + *

+ *

+ * If any observer method of a {@code ProcessObserverMethod} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + * @see ObserverMethod + * @author Gavin King + * @author David Allen + * @author Antoine Sabot-Durand + * @param The type of the event being observed + * @param The bean type containing the observer method + */ +public interface ProcessObserverMethod { + + /** + * The {@link javax.enterprise.inject.spi.AnnotatedMethod} representing the observer method. + * + *

+ * If invoked upon a {@link ProcessSyntheticObserverMethod} event, non-portable behavior results and the returned value should be ignored. + *

+ * + * @return the {@link javax.enterprise.inject.spi.AnnotatedMethod} representing the observer method + * @throws IllegalStateException if called outside of the observer method invocation + */ + public AnnotatedMethod getAnnotatedMethod(); + + /** + * The {@link javax.enterprise.inject.spi.ObserverMethod} object that will be used by the container to invoke the observer + * when a matching event is fired. + * + * @return the {@link javax.enterprise.inject.spi.ObserverMethod} object that will be used by the container to call the + * observer method + * @throws IllegalStateException if called outside of the observer method invocation + */ + public ObserverMethod getObserverMethod(); + + /** + * Registers a definition error with the container, causing the container to abort deployment after bean discovery is + * complete. + * + * @param t A {@link java.lang.Throwable} representing the definition error + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addDefinitionError(Throwable t); + + /** + * Replaces the {@link javax.enterprise.inject.spi.ObserverMethod}. + * + * @param observerMethod the new {@link javax.enterprise.inject.spi.ObserverMethod} object to use + * @throws IllegalStateException if called outside of the observer method invocation + * + * @since 2.0 + */ + public void setObserverMethod(ObserverMethod observerMethod); + + /** + * Returns a {@link ObserverMethodConfigurator} initialized with the {@link ObserverMethod} processed by this event, + * to configure a new ObserverMethod that will replace the original one at the end of the observer invocation. + * + * Each call returns the same ObserverMethodConfigurator + * + * @return a non reusable {@link ObserverMethodConfigurator} to configure the replacing ObserverMethod + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public ObserverMethodConfigurator configureObserverMethod(); + + /** + * Forces the container to ignore the observer method. + * + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public void veto(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessProducer.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessProducer.java new file mode 100644 index 000000000..0f2c1d6dd --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessProducer.java @@ -0,0 +1,104 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.inject.spi.configurator.ProducerConfigurator; + +/** + *

+ * The container fires an event of this type for each {@linkplain javax.enterprise.inject.Produces producer method or field} of + * each enabled bean, including resources. + *

+ *

+ * Any observer of this event is permitted to wrap and/or replace the {@code Producer} by calling either + * {@link #setProducer(Producer)} or {@link #configureProducer()}. If both methods are called within an observer notification an + * {@link IllegalStateException} is thrown. The container must use the final value of this property, after all observers have + * been called, whenever it calls the producer or disposer. + *

+ *

+ * For example, this observer decorates the {@code Producer} for the all producer methods and field of type + * {@code EntityManager}. + *

+ * + *
+ * void decorateEntityManager(@Observes ProcessProducer<?, EntityManager> pp) {
+ *     pit.setProducer(decorate(pp.getProducer()));
+ * }
+ * 
+ *

+ * If any observer method of a {@code ProcessProducer} event throws an exception, the exception is treated as a definition error + * by the container. + *

+ * + * @see Producer + * @author David Allen + * @param The bean class of the bean that declares the producer method or field + * @param The return type of the producer method or the type of the producer field + */ +public interface ProcessProducer { + /** + * Returns the {@link javax.enterprise.inject.spi.AnnotatedField} representing the producer field or the + * {@link javax.enterprise.inject.spi.AnnotatedMethod} representing the producer method. + * + * @return the {@link javax.enterprise.inject.spi.AnnotatedMember} representing the producer + * @throws IllegalStateException if called outside of the observer method invocation + */ + public AnnotatedMember getAnnotatedMember(); + + /** + * Returns the {@link javax.enterprise.inject.spi.Producer} object that will be used by the container to call the producer + * method or read the producer field. + * + * @return the {@link javax.enterprise.inject.spi.Producer} invoker object used by the container + * @throws IllegalStateException if called outside of the observer method invocation + */ + public Producer getProducer(); + + /** + * Replaces the {@link javax.enterprise.inject.spi.Producer} object that will be used by the container to call the producer + * method or read the producer field. + * + * @param producer the new {@link javax.enterprise.inject.spi.Producer} object to use + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void setProducer(Producer producer); + + /** + * Returns a {@link ProducerConfigurator} initialized with the {@link Producer} processed by this event, to configure a new + * {@link Producer} that will replace the original one at the end of the observer invocation. + * + *

+ * Each call returns the same configurator instance within an observer notification. + *

+ * + * @return a non reusable {@link ProducerConfigurator} to configure the original + * {@link javax.enterprise.inject.spi.Producer}. + * @throws IllegalStateException if called outside of the observer method invocation + * @since 2.0 + */ + public ProducerConfigurator configureProducer(); + + /** + * Registers a definition error with the container, causing the container to abort deployment after bean discovery is + * complete. + * + * @param t The definition error to register as a {@link java.lang.Throwable} + * @throws IllegalStateException if called outside of the observer method invocation + */ + public void addDefinitionError(Throwable t); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessProducerField.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessProducerField.java new file mode 100644 index 000000000..dd43b4d3c --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessProducerField.java @@ -0,0 +1,52 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The container fires an event of this type for each enabled producer field, before registering the + * {@link javax.enterprise.inject.spi.Bean} object. Resources are considered to be producer fields. + *

+ *

+ * If any observer method of a {@code ProcessProducerField} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + * @author David Allen + * @param The type of the producer field + * @param The class of the bean declaring the producer field + * + */ +public interface ProcessProducerField extends ProcessBean { + /** + * Returns the {@link javax.enterprise.inject.spi.AnnotatedField} representing the producer field. + * + * @return the {@link javax.enterprise.inject.spi.AnnotatedField} for the producer field being registered + * @throws IllegalStateException if called outside of the observer method invocation + */ + public AnnotatedField getAnnotatedProducerField(); + + /** + * Returns the {@link javax.enterprise.inject.spi.AnnotatedParameter} for any matching injection point of the same type as + * the producer field return type found on a disposal method. + * + * @return the disposal method's {@link javax.enterprise.inject.spi.AnnotatedParameter} + * @throws IllegalStateException if called outside of the observer method invocation + * @since 1.1 + */ + public AnnotatedParameter getAnnotatedDisposedParameter(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessProducerMethod.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessProducerMethod.java new file mode 100644 index 000000000..727dc74a5 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessProducerMethod.java @@ -0,0 +1,50 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The container fires an event of this type for each enabled producer method, before registering the + * {@link javax.enterprise.inject.spi.Bean} object. + *

+ *

+ * If any observer method of a {@code ProcessProducerMethod} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + * @author David Allen + * @param The return type of the producer method + * @param The class of the bean declaring the producer method + */ +public interface ProcessProducerMethod extends ProcessBean { + /** + * Returns the {@link javax.enterprise.inject.spi.AnnotatedMethod} representing the producer method. + * + * @return the {@link javax.enterprise.inject.spi.AnnotatedMethod} for the producer method being registered + * @throws IllegalStateException if called outside of the observer method invocation + */ + public AnnotatedMethod getAnnotatedProducerMethod(); + + /** + * Returns the {@link javax.enterprise.inject.spi.AnnotatedParameter} for any matching injection point of the same type as + * the producer method return type found on a disposal method. + * + * @return the disposal method's {@link javax.enterprise.inject.spi.AnnotatedParameter} + * @throws IllegalStateException if called outside of the observer method invocation + */ + public AnnotatedParameter getAnnotatedDisposedParameter(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSessionBean.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSessionBean.java new file mode 100644 index 000000000..f6eaf6cb7 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSessionBean.java @@ -0,0 +1,56 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The container fires an event of this type for each enabled session bean, before registering the + * {@link javax.enterprise.inject.spi.Bean} object. + *

+ * + *

+ * If any observer method of a {@code ProcessSessionBean} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + *

+ * Note that the type parameter of the super-interface of {@link ProcessSessionBean} is {@link Object} as {@link ProcessBean} + * allows you access to the {@link Bean}, which in turn allows you to instantiate an instance, which, for interface-view EJBs + * will not be an instance of X. + *

+ * + * @author David Allen + * @param + */ +public interface ProcessSessionBean extends ProcessManagedBean { + /** + * Returns the EJB name of the session bean. + * + * @return the name of the EJB + * @throws IllegalStateException if called outside of the observer method invocation + */ + public String getEjbName(); + + /** + * Returns a {@link javax.enterprise.inject.spi.SessionBeanType} representing the kind of session bean. + * + * @return the {@link javax.enterprise.inject.spi.SessionBeanType} + * @throws IllegalStateException if called outside of the observer method invocation + */ + public SessionBeanType getSessionBeanType(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSyntheticAnnotatedType.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSyntheticAnnotatedType.java new file mode 100644 index 000000000..b5dd2fa24 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSyntheticAnnotatedType.java @@ -0,0 +1,62 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The container fires an event of this type for each Java class or interface added by + * {@link BeforeBeanDiscovery#addAnnotatedType(AnnotatedType)}, + * {@link BeforeBeanDiscovery#addAnnotatedType(AnnotatedType, String)} or + * {@link javax.enterprise.inject.spi.AfterTypeDiscovery#addAnnotatedType(AnnotatedType, String)} + *

+ *

+ * Any observer of this event is permitted to wrap and/or replace the {@link javax.enterprise.inject.spi.AnnotatedType}. The + * container must use the final value of this property, after all observers have been called, to discover the types and read the + * annotations of the program elements. + *

+ *

+ * For example, the following observer decorates the {@link javax.enterprise.inject.spi.AnnotatedType} for every class that is + * added by {@link BeforeBeanDiscovery#addAnnotatedType(AnnotatedType)}. + *

+ * + *
+ * public <T> void decorateAnnotatedType(@Observes ProcessSyntheticAnnotatedType<T> pat) {
+ *     pat.setAnnotatedType(decorate(pat.getAnnotatedType()));
+ * }
+ * 
+ *

+ * If any observer method of a {@code ProcessSyntheticAnnotatedType} event throws an exception, the exception is treated as a + * definition error by the container. + *

+ * + * @author David Allen + * @author Pete Muir + * @see AnnotatedType + * @see ProcessAnnotatedType + * @param The class being annotated + * @since 1.1 + */ +public interface ProcessSyntheticAnnotatedType extends ProcessAnnotatedType { + /** + * Get the extension instance which added the {@link AnnotatedType} for which this event is being fired. + * + * @return the extension instance + * @throws IllegalStateException if called outside of the observer method invocation + */ + public Extension getSource(); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSyntheticBean.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSyntheticBean.java new file mode 100644 index 000000000..f254e9cfe --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSyntheticBean.java @@ -0,0 +1,44 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The container fires an event of this type for each custom bean implementation added through + * {@link AfterBeanDiscovery#addBean()} or {@link AfterBeanDiscovery#addBean(Bean)}, before registering the + * {@link javax.enterprise.inject.spi.Bean} object. + *

+ *

+ * If any observer method of a {@code ProcessSyntheticBean} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + * @author Martin Kouba + * @param The class of the bean + * @since 2.0 + */ +public interface ProcessSyntheticBean extends ProcessBean { + + /** + * Get the extension instance which added the {@link Bean} for which this event is being fired. + * + * @return the extension instance + * @throws IllegalStateException if called outside of the observer method invocation + */ + public Extension getSource(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSyntheticObserverMethod.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSyntheticObserverMethod.java new file mode 100644 index 000000000..2fd8691cd --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProcessSyntheticObserverMethod.java @@ -0,0 +1,47 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * The container fires an event of this type for each custom implementation of {@link ObserverMethod} added through + * {@link AfterBeanDiscovery#addObserverMethod(ObserverMethod)} or {@link AfterBeanDiscovery#addObserverMethod()}, before + * registering the {@link javax.enterprise.inject.spi.ObserverMethod} object. + *

+ *

+ * If any observer method of a {@code ProcessSyntheticObserverMethod} event throws an exception, the exception is treated as a definition + * error by the container. + *

+ * + * @see ObserverMethod + * @author Martin Kouba + * @param The type of the event being observed + * @param The bean type containing the observer method, i.e. {@link ObserverMethod#getBeanClass()} + * @since 2.0 + */ +public interface ProcessSyntheticObserverMethod extends ProcessObserverMethod { + + /** + * Get the extension instance which added the {@link ObserverMethod} for which this event is being fired. + * + * @return the extension instance + * @throws IllegalStateException if called outside of the observer method invocation + */ + public Extension getSource(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Producer.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Producer.java new file mode 100644 index 000000000..2b4e17983 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Producer.java @@ -0,0 +1,80 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import java.util.Set; + +import javax.enterprise.context.spi.CreationalContext; + +/** + *

+ * Provides a generic operation for producing an instance of a type. + *

+ * + * @author Pete Muir + * @author David Allen + * @param The class of object produced by the producer + */ +public interface Producer { + /** + *

+ * Causes an instance to be produced via the {@code Producer}. + *

+ *

+ * If the {@code Producer} represents a class, this will invoke the constructor annotated {@link javax.inject.Inject} if it + * exists, or the constructor with no parameters otherwise. If the class has interceptors, produce() is responsible + * for building the interceptors and decorators of the instance. + *

+ *

+ * If the {@code Producer} represents a producer field or method, this will invoke the producer method on, or access the + * producer field of, a contextual instance of the bean that declares the producer. + *

+ * + * @param ctx The {@link javax.enterprise.context.spi.CreationalContext} to use for the produced object + * @return the instance produced + */ + public T produce(CreationalContext ctx); + + /** + *

+ * Destroys the instance. + *

+ *

+ * If the {@code Producer} represents a class, then this operation does nothing. + *

+ *

+ * If the {@code Producer} represents a producer field or method, this calls the disposer method, if any, on a contextual + * instance of the bean that declares the disposer method or performs any additional required cleanup, if any, to destroy + * state associated with a resource. + *

+ * + * @param instance The instance to dispose + */ + public void dispose(T instance); + + /** + *

+ * Returns the set of all {@code InjectionPoints}. If the {@code Producer} represents a class, then this returns returns the + * set of {@code InjectionPoint} objects representing all injected fields, bean constructor parameters and initializer + * method parameters. For a producer method, this returns the set of {@code InjectionPoint} objects representing all + * parameters of the producer method. + *

+ * + * @return the set of all {@linkplain javax.enterprise.inject.spi.InjectionPoint injection points} for the producer + */ + public Set getInjectionPoints(); +} \ No newline at end of file diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProducerFactory.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProducerFactory.java new file mode 100644 index 000000000..cffc16c71 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/ProducerFactory.java @@ -0,0 +1,59 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + *

+ * An {@link ProducerFactory} can create an {@link Producer} for a given bean. + *

+ * + *

+ * The {@link ProducerFactory} obtained from {@link BeanManager#getProducerFactory(AnnotatedMethod, Bean)} or + * {@link BeanManager#getProducerFactory(AnnotatedField, Bean)} is capable of providing container created + * producers. This factory can be wrapped to add behavior to container created producers. + *

+ * + *

+ * For example: + *

+ * + *
+ * BeanAttributes<MyBean> myBeanAttributes = beanManager.createBeanAttributes(myBeanAnnotatedFieldField);
+ * beanManager.createBean(myBeanAttributes, MyBean.class, new ProducerFactory() {
+ * 
+ *     public <T> Producer<T> createProducer(Bean<T> bean) {
+ *         return new WrappingProducer<T>(beanManager.getProducerFactory(myBeanAnnotatedField).createProducer(bean));
+ *     }
+ * });
+ * 
+ * + * @author Pete Muir + * @since 1.1 + * @param type of the bean containing the producer + */ +public interface ProducerFactory { + + /** + * Create a new producer for a bean. + * + * @param bean the bean to create the producer for, or null if creating a non-contextual object + * @return the producer + */ + public Producer createProducer(Bean bean); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/SecurityActions.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/SecurityActions.java new file mode 100644 index 000000000..579a0a9a9 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/SecurityActions.java @@ -0,0 +1,42 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2018, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * Licensed under the Apache License, Version 2.1-SNAPSHOT (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.1-SNAPSHOT + * 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 javax.enterprise.inject.spi; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ServiceLoader; + +/** + * + * This utility class is used to optimize invocation made through the SecurityManager + * + * @author Antoine Sabot-durand + */ + +final class SecurityActions { + + private SecurityActions() { + + } + + static ServiceLoader loadService(Class service, ClassLoader classLoader) { + return AccessController.doPrivileged( + (PrivilegedAction>) () -> ServiceLoader.load(service, classLoader) + ); + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/SessionBeanType.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/SessionBeanType.java new file mode 100644 index 000000000..14471ad72 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/SessionBeanType.java @@ -0,0 +1,38 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +/** + * Identifies the kind of EJB session bean. + * + * @author Gavin King + * + */ +public enum SessionBeanType { + /** + * A {@linkplain javax.ejb.Stateless stateless} session bean + */ + STATELESS, + /** + * A {@linkplain javax.ejb.Stateful stateful} session bean + */ + STATEFUL, + /** + * A {@linkplain javax.ejb.Singleton singleton} session bean + */ + SINGLETON +} \ No newline at end of file diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Unmanaged.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Unmanaged.java new file mode 100644 index 000000000..5b1719e01 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/Unmanaged.java @@ -0,0 +1,199 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import javax.enterprise.context.spi.CreationalContext; + +/** + *

+ * Helper class for injecting and calling lifecycle callbacks unmanaged instances for use by framework and library integrators. + *

+ * + *
+ * Unmanaged<Foo> unmanagedFoo = new Unmanaged<Foo>(Foo.class);
+ * UnmanagedInstance<Foo> fooInstance = unmanagedFoo.newInstance();
+ * Foo foo = fooInstance.produce().inject().postConstruct().get();
+ * ... // Use the foo instance
+ * fooInstance.preDestroy().dispose();
+ * 
+ * + *

+ * An instance of this class can be safely held for the lifetime of the application. + *

+ * + *

+ * {@link UnmanagedInstance}s created by this class are not suitable for sharing between threads. + *

+ * + * @author Pete Muir + * @since 1.1 + * @param type of unmanaged instances + */ +public class Unmanaged { + + private final InjectionTarget injectionTarget; + private final BeanManager beanManager; + + /** + * Create an injector for the given class + * @param manager the {@link BeanManager} + * @param clazz class of the unmanaged instances + */ + public Unmanaged(BeanManager manager, Class clazz) { + this.beanManager = manager; + AnnotatedType type = manager.createAnnotatedType(clazz); + this.injectionTarget = manager.getInjectionTargetFactory(type).createInjectionTarget(null); + } + + /** + * Create an injector for the given class, using the current bean manager + * @param clazz class of the unmanaged instances + */ + public Unmanaged(Class clazz) { + this(CDI.current().getBeanManager(), clazz); + } + + /** + * Instantiate a new UnmanagedInstance + * + * @return a new {@link UnmanagedInstance} + */ + public UnmanagedInstance newInstance() { + return new UnmanagedInstance<>(beanManager, injectionTarget); + } + + /** + * Represents a non-contextual instance. + * + * @see Unmanaged + */ + public static class UnmanagedInstance { + + private final CreationalContext ctx; + private final InjectionTarget injectionTarget; + private T instance; + private boolean disposed = false; + + private UnmanagedInstance(BeanManager beanManager, InjectionTarget injectionTarget) { + this.injectionTarget = injectionTarget; + this.ctx = beanManager.createCreationalContext(null); + } + + /** + * Get the instance + * @return the instance + */ + public T get() { + return instance; + } + + /** + * Create the instance + * + * @throws IllegalStateException if produce() is called on an already produced instance + * @throws IllegalStateException if produce() is called on an instance that has already been disposed + * @return self + */ + public UnmanagedInstance produce() { + if (instance != null) { + throw new IllegalStateException("Trying to call produce() on already constructed instance"); + } + if (disposed) { + throw new IllegalStateException("Trying to call produce() on an already disposed instance"); + } + this.instance = injectionTarget.produce(ctx); + return this; + } + + /** + * Inject the instance + * + * @throws IllegalStateException if inject() is called before produce() is called + * @throws IllegalStateException if inject() is called on an instance that has already been disposed + * @return self + */ + public UnmanagedInstance inject() { + if (instance == null) { + throw new IllegalStateException("Trying to call inject() before produce() was called"); + } + if (disposed) { + throw new IllegalStateException("Trying to call inject() on already disposed instance"); + } + injectionTarget.inject(instance, ctx); + return this; + } + + /** + * Call the @PostConstruct callback + * + * @throws IllegalStateException if postConstruct() is called before produce() is called + * @throws IllegalStateException if postConstruct() is called on an instance that has already been disposed + * @return self + */ + public UnmanagedInstance postConstruct() { + if (instance == null) { + throw new IllegalStateException("Trying to call postConstruct() before produce() was called"); + } + if (disposed) { + throw new IllegalStateException("Trying to call postConstruct() on already disposed instance"); + } + injectionTarget.postConstruct(instance); + return this; + } + + /** + * Call the @PreDestroy callback + * + * @throws IllegalStateException if preDestroy() is called before produce() is called + * @throws IllegalStateException if preDestroy() is called on an instance that has already been disposed + * @return self + */ + public UnmanagedInstance preDestroy() { + if (instance == null) { + throw new IllegalStateException("Trying to call preDestroy() before produce() was called"); + } + if (disposed) { + throw new IllegalStateException("Trying to call preDestroy() on already disposed instance"); + } + injectionTarget.preDestroy(instance); + return this; + } + + /** + * Dispose of the instance, doing any necessary cleanup + * + * @throws IllegalStateException if dispose() is called before produce() is called + * @throws IllegalStateException if dispose() is called on an instance that has already been disposed + * @return self + */ + public UnmanagedInstance dispose() { + if (instance == null) { + throw new IllegalStateException("Trying to call dispose() before produce() was called"); + } + if (disposed) { + throw new IllegalStateException("Trying to call dispose() on already disposed instance"); + } + disposed = true; + injectionTarget.dispose(instance); + ctx.release(); + return this; + } + + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/WithAnnotations.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/WithAnnotations.java new file mode 100644 index 000000000..3fec6d364 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/WithAnnotations.java @@ -0,0 +1,49 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + *

+ * {@link WithAnnotations} may be applied to any portable extension observer method with an event parameter type of + * {@link ProcessAnnotatedType} to filter the events delivered. + *

+ * + *

+ * If the {@link WithAnnotations} annotation is applied to a portable extension observer method, then only + * {@link ProcessAnnotatedType} events for types which have at least one of the annotations specified are observed. The + * annotation can appear on the annotated type, or on any member, or any parameter of any member of the annotated type, as defined + * in section 11.4 Alternative metadata sources. + * The annotation may be applied as a meta-annotation on any annotation considered. + * + * @author Pete Muir + * @since 1.1 + */ +@Retention(RUNTIME) +@Target(PARAMETER) +public @interface WithAnnotations { + + Class[] value(); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedConstructorConfigurator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedConstructorConfigurator.java new file mode 100644 index 000000000..f09238b5c --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedConstructorConfigurator.java @@ -0,0 +1,107 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi.configurator; + +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import javax.enterprise.inject.spi.AnnotatedConstructor; +import javax.enterprise.inject.spi.AnnotatedParameter; + +/** + * + * This interface is part of the {@link AnnotatedTypeConfigurator} SPI and helps defining an {@link AnnotatedConstructor} + * + * @author Martin Kouba + * @author Antoine Sabot-Durand + * @since 2.0 + * @param the class declaring the constructor + */ +public interface AnnotatedConstructorConfigurator { + + /** + * + * @return the original {@link AnnotatedConstructor} + */ + AnnotatedConstructor getAnnotated(); + + /** + * Add an annotation to the constructor. + * + * @param annotation the annotation to add + * @return self + */ + AnnotatedConstructorConfigurator add(Annotation annotation); + + /** + * Remove annotations that match the specified predicate. + * + *

+ * Example predicates: + *

+ * + *
+     *  {@code
+     * // To remove all the annotations:
+     * (a) -> true
+     * 
+     * // To remove annotations with a concrete annotation type:
+     * (a) -> a.annotationType().equals(Foo.class)
+     * 
+     * // To remove annotation equal to a specified object:
+     * (a) -> a.equals(fooAnnotation)
+     * 
+     * // To remove annotations that are considered equivalent for the purposes of typesafe resolution:
+     * (a) -> beanManager.areQualifiersEquivalent(a, fooQualifier)
+     * (a) -> beanManager.areInterceptorBindingsEquivalent(a, fooInterceptorBinding)
+     * }
+     * 
+ * + * @param predicate {@link Predicate} used to filter annotations to remove + * @return self + */ + AnnotatedConstructorConfigurator remove(Predicate predicate); + + /** + * Remove all the annotations. + * + * @return self + */ + default AnnotatedConstructorConfigurator removeAll() { + return remove((a) -> true); + } + + /** + * + * @return an immutable list of {@link AnnotatedParameterConfigurator}s reflecting the + * {@link AnnotatedConstructor#getParameters()} + */ + List> params(); + + /** + * + * @param predicate Testing the original {@link AnnotatedParameter} + * @return a sequence of {@link AnnotatedParameterConfigurator}s matching the given predicate + * @see AnnotatedParameterConfigurator#getAnnotated() + */ + default Stream> filterParams(Predicate> predicate) { + return params().stream().filter(p -> predicate.test(p.getAnnotated())); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedFieldConfigurator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedFieldConfigurator.java new file mode 100644 index 000000000..b128284d9 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedFieldConfigurator.java @@ -0,0 +1,86 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi.configurator; + +import java.lang.annotation.Annotation; +import java.util.function.Predicate; + +import javax.enterprise.inject.spi.AnnotatedField; + +/** + * This interface is part of the {@link AnnotatedTypeConfigurator} SPI and helps defining an {@link AnnotatedField} + * + * @author Martin Kouba + * @author Antoine Sabot-Durand + * @since 2.0 + * @param the class declaring the field + */ +public interface AnnotatedFieldConfigurator { + + /** + * + * @return the original {@link AnnotatedField} + */ + AnnotatedField getAnnotated(); + + /** + * Add an annotation to the field. + * + * @param annotation the annotation to add + * @return self + */ + AnnotatedFieldConfigurator add(Annotation annotation); + + /** + * Remove annotations that match the specified predicate. + * + *

+ * Example predicates: + *

+ * + *
+     *  {@code
+     * // To remove all the annotations:
+     * (a) -> true
+     * 
+     * // To remove annotations with a concrete annotation type:
+     * (a) -> a.annotationType().equals(Foo.class)
+     * 
+     * // To remove annotation equal to a specified object:
+     * (a) -> a.equals(fooAnnotation)
+     * 
+     * // To remove annotations that are considered equivalent for the purposes of typesafe resolution:
+     * (a) -> beanManager.areQualifiersEquivalent(a, fooQualifier)
+     * (a) -> beanManager.areInterceptorBindingsEquivalent(a, fooInterceptorBinding)
+     * }
+     * 
+ * + * @param predicate {@link Predicate} used to filter annotations to remove + * @return self + */ + AnnotatedFieldConfigurator remove(Predicate predicate); + + /** + * Remove all the annotations. + * + * @return self + */ + default AnnotatedFieldConfigurator removeAll() { + return remove((a) -> true); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedMethodConfigurator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedMethodConfigurator.java new file mode 100644 index 000000000..fecc9c9e7 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedMethodConfigurator.java @@ -0,0 +1,106 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi.configurator; + +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import javax.enterprise.inject.spi.AnnotatedMethod; +import javax.enterprise.inject.spi.AnnotatedParameter; + +/** + * This interface is part of the {@link AnnotatedTypeConfigurator} SPI and helps defining an {@link AnnotatedMethod} + * + * @author Martin Kouba + * @author Antoine Sabot-Durand + * @since 2.0 + * @param the class declaring the method + */ +public interface AnnotatedMethodConfigurator { + + /** + * + * @return the original {@link AnnotatedMethod} + */ + AnnotatedMethod getAnnotated(); + + /** + * Add an annotation to the method. + * + * @param annotation the annotation to add + * @return self + */ + AnnotatedMethodConfigurator add(Annotation annotation); + + /** + * Remove annotations that match the specified predicate. + * + *

+ * Example predicates: + *

+ * + *
+     *  {@code
+     * // To remove all the annotations:
+     * (a) -> true
+     * 
+     * // To remove annotations with a concrete annotation type:
+     * (a) -> a.annotationType().equals(Foo.class)
+     * 
+     * // To remove annotation equal to a specified object:
+     * (a) -> a.equals(fooAnnotation)
+     * 
+     * // To remove annotations that are considered equivalent for the purposes of typesafe resolution:
+     * (a) -> beanManager.areQualifiersEquivalent(a, fooQualifier)
+     * (a) -> beanManager.areInterceptorBindingsEquivalent(a, fooInterceptorBinding)
+     * }
+     * 
+ * + * @param predicate {@link Predicate} used to filter annotations to remove + * @return self + */ + AnnotatedMethodConfigurator remove(Predicate predicate); + + /** + * Remove all the annotations. + * + * @return self + */ + default AnnotatedMethodConfigurator removeAll() { + return remove((a) -> true); + } + + /** + * + * @return an immutable list of {@link AnnotatedParameterConfigurator}s reflecting the + * {@link AnnotatedMethod#getParameters()} + */ + List> params(); + + /** + * + * @param predicate Testing the original {@link AnnotatedParameter} + * @return a sequence of {@link AnnotatedParameterConfigurator}s matching the given predicate + * @see AnnotatedParameterConfigurator#getAnnotated() + */ + default Stream> filterParams(Predicate> predicate) { + return params().stream().filter(p -> predicate.test(p.getAnnotated())); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedParameterConfigurator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedParameterConfigurator.java new file mode 100644 index 000000000..0e8088784 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedParameterConfigurator.java @@ -0,0 +1,87 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi.configurator; + +import java.lang.annotation.Annotation; +import java.util.function.Predicate; + +import javax.enterprise.inject.spi.AnnotatedParameter; + +/** + * + * This interface is part of the {@link AnnotatedTypeConfigurator} SPI and helps defining an {@link AnnotatedParameter} + * + * @author Martin Kouba + * @author Antoine Sabot-Durand + * @since 2.0 + * @param the class containing the method declaring the parameter + */ +public interface AnnotatedParameterConfigurator { + + /** + * + * @return the original {@link AnnotatedParameter} + */ + AnnotatedParameter getAnnotated(); + + /** + * Add an annotation to the parameter. + * + * @param annotation the annotation to add + * @return self + */ + AnnotatedParameterConfigurator add(Annotation annotation); + + /** + * Remove annotations that match the specified predicate. + * + *

+ * Example predicates: + *

+ * + *
+     *  {@code
+     * // To remove all the annotations:
+     * (a) -> true
+     * 
+     * // To remove annotations with a concrete annotation type:
+     * (a) -> a.annotationType().equals(Foo.class)
+     * 
+     * // To remove annotation equal to a specified object:
+     * (a) -> a.equals(fooAnnotation)
+     * 
+     * // To remove annotations that are considered equivalent for the purposes of typesafe resolution:
+     * (a) -> beanManager.areQualifiersEquivalent(a, fooQualifier)
+     * (a) -> beanManager.areInterceptorBindingsEquivalent(a, fooInterceptorBinding)
+     * }
+     * 
+ * + * @param predicate {@link Predicate} used to filter annotations to remove + * @return self + */ + AnnotatedParameterConfigurator remove(Predicate predicate); + + /** + * Remove all the annotations. + * + * @return self + */ + default AnnotatedParameterConfigurator removeAll() { + return remove((a) -> true); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedTypeConfigurator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedTypeConfigurator.java new file mode 100644 index 000000000..1d4a2b706 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/AnnotatedTypeConfigurator.java @@ -0,0 +1,156 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi.configurator; + +import java.lang.annotation.Annotation; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import javax.enterprise.inject.spi.AfterTypeDiscovery; +import javax.enterprise.inject.spi.AnnotatedConstructor; +import javax.enterprise.inject.spi.AnnotatedField; +import javax.enterprise.inject.spi.AnnotatedMethod; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.BeforeBeanDiscovery; +import javax.enterprise.inject.spi.ProcessAnnotatedType; + +/** + *

+ * This API is a helper to configure a new {@link AnnotatedType} instance. The container must provide an implementation of + * this interface. + *

+ * + *

+ * AnnotatedTypeConfigurator is not reusable. + *

+ * + *

+ * This configurator is not thread safe and shall not be used concurrently. + *

+ * + * @see BeforeBeanDiscovery#addAnnotatedType(Class, String) + * @see AfterTypeDiscovery#addAnnotatedType(Class, String) + * @see ProcessAnnotatedType#configureAnnotatedType() + * @param the class represented by the configured AnnotatedType + * @author Martin Kouba + * @author Antoine Sabot-Durand + * @since 2.0 + */ +public interface AnnotatedTypeConfigurator { + + /** + * + * @return the original {@link AnnotatedType} + */ + AnnotatedType getAnnotated(); + + /** + * Add an annotation to the type. + * + * @param annotation the annotation to add + * @return self + */ + AnnotatedTypeConfigurator add(Annotation annotation); + + /** + * Remove annotations that match the specified predicate. + * + *

+ * Example predicates: + *

+ * + *
+     *  {@code
+     * // To remove all the annotations:
+     * (a) -> true
+     * 
+     * // To remove annotations with a concrete annotation type:
+     * (a) -> a.annotationType().equals(Foo.class)
+     * 
+     * // To remove annotation equal to a specified object:
+     * (a) -> a.equals(fooAnnotation)
+     * 
+     * // To remove annotations that are considered equivalent for the purposes of typesafe resolution:
+     * (a) -> beanManager.areQualifiersEquivalent(a, fooQualifier)
+     * (a) -> beanManager.areInterceptorBindingsEquivalent(a, fooInterceptorBinding)
+     * }
+     * 
+ * + * @param predicate {@link Predicate} used to filter annotations to remove + * @return self + */ + AnnotatedTypeConfigurator remove(Predicate predicate); + + /** + * Remove all the annotations. + * + * @return self + */ + default AnnotatedTypeConfigurator removeAll() { + return remove((a) -> true); + } + + /** + * + * @return an immutable set of {@link AnnotatedMethodConfigurator}s reflecting the {@link AnnotatedType#getMethods()} + */ + Set> methods(); + + /** + * @param predicate Testing the original {@link AnnotatedMethod} + * @return a sequence of {@link AnnotatedMethodConfigurator}s matching the given predicate + * @see AnnotatedMethodConfigurator#getAnnotated() + */ + default Stream> filterMethods(Predicate> predicate) { + return methods().stream().filter(c -> predicate.test(c.getAnnotated())); + } + + /** + * + * @return an immutable set of {@link AnnotatedFieldConfigurator}s reflecting the {@link AnnotatedType#getFields()} + */ + Set> fields(); + + /** + * @param predicate Testing the original {@link AnnotatedField} + * @return a sequence of {@link AnnotatedFieldConfigurator}s matching the given predicate + * @see AnnotatedFieldConfigurator#getAnnotated() + */ + default Stream> filterFields(Predicate> predicate) { + return fields().stream().filter(f -> predicate.test(f.getAnnotated())); + } + + /** + * + * @return an immutable set of {@link AnnotatedConstructorConfigurator}s reflecting the + * {@link AnnotatedType#getConstructors()} + */ + Set> constructors(); + + /** + * + * @param predicate Testing the original {@link AnnotatedConstructor} + * @return a sequence of {@link AnnotatedConstructorConfigurator}s matching the given predicate + * @see AnnotatedConstructorConfigurator#getAnnotated() + */ + default Stream> filterConstructors(Predicate> predicate) { + return constructors().stream().filter(c -> predicate.test(c.getAnnotated())); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/BeanAttributesConfigurator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/BeanAttributesConfigurator.java new file mode 100644 index 000000000..47510c1f8 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/BeanAttributesConfigurator.java @@ -0,0 +1,200 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2015, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi.configurator; + +import javax.enterprise.inject.spi.BeanAttributes; +import javax.enterprise.inject.spi.ProcessBeanAttributes; +import javax.enterprise.util.TypeLiteral; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Set; + +/** + * This API is an helper to configure a new {@link BeanAttributes} instance. + * CDI container must provides an implementation of this interface. + * + * This configurator is not thread safe and shall not be used concurrently. + * + * @see ProcessBeanAttributes#configureBeanAttributes() + * @param the class of the bean instance + * @author Antoine Sabot-Durand + * @since 2.0 + */ +public interface BeanAttributesConfigurator { + + /** + * + * Add a type to the bean types + * + * @param type the type to add + * @return self + */ + BeanAttributesConfigurator addType(Type type); + + /** + * + * Add a type to the bean types + * + * @param typeLiteral the type to add + * @return self + */ + BeanAttributesConfigurator addType(TypeLiteral typeLiteral); + + /** + * + * Add types to the bean types + * + * @param types types to add + * @return self + */ + BeanAttributesConfigurator addTypes(Type... types); + + /** + * + * Add types to the bean types + * + * @param types types to add + * @return self + */ + BeanAttributesConfigurator addTypes(Set types); + + /** + * Adds an unrestricted set of bean types for the given type as if it represented a bean class of a managed bean. + * Illegal bean types are omitted. + * + * @param type to build the closure from + * @return self + */ + BeanAttributesConfigurator addTransitiveTypeClosure(Type type); + + /** + * + * Replace bean types + * + * @param types the types of the configured bean + * @return self + */ + BeanAttributesConfigurator types(Type... types); + + /** + * + * Replace bean types + * + * @param types the types of the configured bean + * @return self + */ + BeanAttributesConfigurator types(Set types); + + /** + * + * Replace Bean scope + * + * @param scope new scope for the configured bean + * @return self + */ + BeanAttributesConfigurator scope(Class scope); + + /** + * + * Add a qualifier to the configured bean + * + * @param qualifier qualifier to add + * @return self + */ + BeanAttributesConfigurator addQualifier(Annotation qualifier); + + /** + * + * Add qualifiers to the bean. + * + * @param qualifiers qualifiers to add + * @return self + */ + BeanAttributesConfigurator addQualifiers(Annotation... qualifiers); + + /** + * + * Add qualifiers to the bean. + * + * @param qualifiers qualifiers to add + * @return self + */ + BeanAttributesConfigurator addQualifiers(Set qualifiers); + + /** + * Replace all qualifiers. + * + * @param qualifiers qualifiers for the build bean + * @return self + */ + BeanAttributesConfigurator qualifiers(Annotation... qualifiers); + + /** + * Replace all qualifiers. + * + * @param qualifiers for the configured bean + * @return self + */ + BeanAttributesConfigurator qualifiers(Set qualifiers); + + /** + * + * Add a stereotype to the configured bean + * + * @param stereotype stereotype to add + * @return self + */ + BeanAttributesConfigurator addStereotype(Class stereotype); + + /** + * + * Add stereotypes to the configured bean + * + * @param stereotypes stereotypes to add + * @return self + */ + BeanAttributesConfigurator addStereotypes(Set> stereotypes); + + /** + * + * Replace stereotypes on the configured bean + * + * @param stereotypes for the configured bean + * @return self + */ + BeanAttributesConfigurator stereotypes(Set> stereotypes); + + /** + * + * Set the name of the configured bean + * + * @param name name for the configured bean + * @return self + */ + BeanAttributesConfigurator name(String name); + + /** + * + * Change the alternative status of the configured bean. + * By default the configured bean is not an alternative. + * + * @param value value for alternative property + * @return self + */ + BeanAttributesConfigurator alternative(boolean value); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/BeanConfigurator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/BeanConfigurator.java new file mode 100644 index 000000000..5f2e17b38 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/BeanConfigurator.java @@ -0,0 +1,316 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2015, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi.configurator; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Function; + +import javax.enterprise.context.spi.Contextual; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.Instance; +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanAttributes; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.PassivationCapable; +import javax.enterprise.util.TypeLiteral; + +/** + * This API is an helper to configure a new {@link Bean} instance. + * CDI container must provides an implementation of this interface. + * + * This builder is not thread safe and shall not be used concurrently. + * + * @see AfterBeanDiscovery#addBean() + * @author Martin Kouba + * @author Antoine Sabot-Durand + * @param the class of the bean instance + * @since 2.0 + */ +public interface BeanConfigurator { + + /** + * Set the class of the configured Bean. + * If not set, the extension class is used. + * + * @param beanClass class of the configured bean + * @return self + */ + BeanConfigurator beanClass(Class beanClass); + + /** + * Add an InjectionPoint to the configured bean + * + * @param injectionPoint the injectionPoint to add + * @return self + */ + BeanConfigurator addInjectionPoint(InjectionPoint injectionPoint); + + /** + * Add InjectionPoints to the configured bean + * + * @param injectionPoints the injectionPoints to add + * @return self + */ + BeanConfigurator addInjectionPoints(InjectionPoint... injectionPoints); + + /** + * Add InjectionPoints to the configured bean + * + * @param injectionPoints the injectionPoints to add + * @return self + */ + BeanConfigurator addInjectionPoints(Set injectionPoints); + + /** + * Replace InjectionPoints for the configured bean + * + * @param injectionPoints the injectionPoints for the configured bean + * @return self + */ + BeanConfigurator injectionPoints(InjectionPoint... injectionPoints); + + /** + * Replace InjectionPoints for the configured bean + * + * @param injectionPoints the injectionPoints for the configured bean + * @return self + */ + BeanConfigurator injectionPoints(Set injectionPoints); + + /** + * Make the configured bean implements {@link PassivationCapable} and its Id for passivation. + * + * + * @param id for + * @see PassivationCapable#getId() + * @return self + */ + BeanConfigurator id(String id); + + /** + * Set a callback to create a bean instance. + * + * @param callback the callback to create the instance + * @return self + * @see Contextual#create(CreationalContext) + */ + BeanConfigurator createWith(Function, U> callback); + + /** + * Set a callback to create a bean instance. + *

+ * The {@link Instance} argument might be used to simulate producer method parameter injection. However, dependent scoped + * bean instances obtained from {@link Instance} during the callback execution remain managed until the produced bean + * instance is destroyed. Therefore, applications are encouraged to always destroy unneeded dependent scoped bean instances + * obtained from {@link Instance}. + * + * @param callback the callback to create the instance + * @return self + */ + BeanConfigurator produceWith(Function, U> callback); + + /** + * Set a callback to destroy a bean instance. + *

+ * If no destroy callback is specified, a NOOP callback is automatically set. + * + * @param callback the callback to destroy the instance + * @return self + */ + BeanConfigurator destroyWith(BiConsumer> callback); + + /** + * Set a callback to destroy a bean instance. + *

+ * If no dispose callback is specified, a NOOP callback is automatically set. + *

+ * The {@link Instance} argument might be used to simulate disposer method parameter injection. All dependent scoped bean + * instances obtained from {@link Instance} during the callback execution are destroyed when the execution completes. + * + * @param callback the callback to dispose the instance + * @return self + */ + BeanConfigurator disposeWith(BiConsumer> callback); + + /** + * Read the information from the given annotated type. All relevant information is overwritten. + * + * @param type class to read information from + * @return self + */ + BeanConfigurator read(AnnotatedType type); + + /** + * Read the information from the given bean attributes. All relevant information is overwritten. + * + * @param beanAttributes beanAttributes to read information from + * @return self + */ + BeanConfigurator read(BeanAttributes beanAttributes); + + /** + * Add a type to the bean types + * + * @param type the type to add + * @return self + */ + BeanConfigurator addType(Type type); + + /** + * Add a type to the bean types + * + * @param typeLiteral the type to add + * @return self + */ + BeanConfigurator addType(TypeLiteral typeLiteral); + + /** + * Add types to the bean types + * + * @param types types to add + * @return self + */ + BeanConfigurator addTypes(Type... types); + + /** + * Add types to the bean types + * + * @param types types to add + * @return self + */ + BeanConfigurator addTypes(Set types); + + /** + * Adds an unrestricted set of bean types for the given type as if it represented a bean class of a managed bean. + * Illegal bean types are omitted. + * + * @param type to build the closure from + * @return self + */ + BeanConfigurator addTransitiveTypeClosure(Type type); + + /** + * Replace bean types + * + * @param types the types of the configured bean + * @return self + */ + BeanConfigurator types(Type... types); + + /** + * Replace bean types + * + * @param types the types of the configured bean + * @return self + */ + BeanConfigurator types(Set types); + + /** + * Replace Bean scope + * + * @param scope new scope for the configured bean + * @return self + */ + BeanConfigurator scope(Class scope); + + /** + * Add a qualifier to the configured bean + * + * @param qualifier qualifier to add + * @return self + */ + BeanConfigurator addQualifier(Annotation qualifier); + + /** + * Add qualifiers to the bean. + * + * @param qualifiers qualifiers to add + * @return self + */ + BeanConfigurator addQualifiers(Annotation... qualifiers); + + /** + * Add qualifiers to the bean. + * + * @param qualifiers qualifiers to add + * @return self + */ + BeanConfigurator addQualifiers(Set qualifiers); + + /** + * Replace all qualifiers. + * + * @param qualifiers qualifiers for the build bean + * @return self + */ + BeanConfigurator qualifiers(Annotation... qualifiers); + + /** + * Replace all qualifiers. + * + * @param qualifiers for the configured bean + * @return self + */ + BeanConfigurator qualifiers(Set qualifiers); + + /** + * Add a stereotype to the configured bean + * + * @param stereotype stereotype to add + * @return self + */ + BeanConfigurator addStereotype(Class stereotype); + + /** + * Add stereotypes to the configured bean + * + * @param stereotypes stereotypes to add + * @return self + */ + BeanConfigurator addStereotypes(Set> stereotypes); + + /** + * Replace stereotypes on the configured bean + * + * @param stereotypes for the configured bean + * @return self + */ + BeanConfigurator stereotypes(Set> stereotypes); + + /** + * Set the name of the configured bean + * + * @param name name for the configured bean + * @return self + */ + BeanConfigurator name(String name); + + /** + * Change the alternative status of the configured bean. + * By default the configured bean is not an alternative. + * + * @param value value for alternative property + * @return self + */ + BeanConfigurator alternative(boolean value); + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/InjectionPointConfigurator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/InjectionPointConfigurator.java new file mode 100644 index 000000000..76ce9072c --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/InjectionPointConfigurator.java @@ -0,0 +1,109 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi.configurator; + +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.ProcessInjectionPoint; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Set; + +/** + * This API is an helper to configure an existing {@link InjectionPoint} instance. + * CDI container must provides an implementation of this interface. + * + * This builder is not thread safe and shall not be used concurrently. + * + * @see ProcessInjectionPoint#configureInjectionPoint() + * @author Antoine Sabot-Durand + * @since 2.0 + */ +public interface InjectionPointConfigurator { + + /** + * Set the required {@link Type} (that will be used during typesafe resolution) + * of the InjectionPoint to build. + * + * @param requiredType for the InjectionPoint to build + * @return self + */ + InjectionPointConfigurator type(Type requiredType); + + /** + * + * Add the qualifier to the InjectionPoint to build + * + * @param qualifier the qualifier to add + * @return self + */ + InjectionPointConfigurator addQualifier(Annotation qualifier); + + /** + * + * Add all the qualifiers to the InjectionPoint to build + * + * @param qualifiers a varargs or array of qualifiers to add + * @return self + */ + InjectionPointConfigurator addQualifiers(Annotation... qualifiers); + + /** + * + * Add all the qualifiers to the InjectionPoint to build + * + * @param qualifiers a Set of qualifiers to add + * @return self + */ + InjectionPointConfigurator addQualifiers(Set qualifiers); + + /** + * Replace all qualifiers. + * + * @param qualifiers a varargs or array of qualifiers to replace to existing ones + * @return self + */ + InjectionPointConfigurator qualifiers(Annotation... qualifiers); + + /** + * Replace all qualifiers. + * + * @param qualifiers a Set of qualifiers to replace to existing ones + * @return self + */ + InjectionPointConfigurator qualifiers(Set qualifiers); + + /** + * + * Change the delegate status of the built InjectionPoint. + * By default the InjectionPoint is not a delegate one. + * + * @param delegate boolean to define or undefine the delegate nature of the configured InjectionPoint + * @return self + */ + InjectionPointConfigurator delegate(boolean delegate); + + /** + * + * Change the transient status of the built InjectionPoint. + * By default the InjectionPoint is not transient. + * + * @param trans boolean to define or undefine the transient nature of the configured InjectionPoint + * @return self + */ + InjectionPointConfigurator transientField(boolean trans); +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/ObserverMethodConfigurator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/ObserverMethodConfigurator.java new file mode 100644 index 000000000..643c87468 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/ObserverMethodConfigurator.java @@ -0,0 +1,191 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi.configurator; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Set; + +import javax.enterprise.event.ObserverException; +import javax.enterprise.event.Reception; +import javax.enterprise.event.TransactionPhase; +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.AnnotatedMethod; +import javax.enterprise.inject.spi.EventContext; +import javax.enterprise.inject.spi.ObserverMethod; +import javax.enterprise.inject.spi.ProcessObserverMethod; + +/** + *

+ * An {@link ObserverMethodConfigurator} can configure an {@link ObserverMethod}. The container must provide an implementation + * of this interface. + *

+ * + *

+ * This configurator is not thread safe and shall not be used concurrently. + *

+ * + * @param type of the event the configured ObserverMethod will observe + * @author Antoine Sabot-Durand + * @see ProcessObserverMethod#configureObserverMethod() + * @see AfterBeanDiscovery#addObserverMethod() + * @since 2.0 + */ +public interface ObserverMethodConfigurator { + + /** + * Read observer meta data from a existing {@link java.lang.reflect.Method} + * + * @param method to read meta data from + * @return self + */ + ObserverMethodConfigurator read(Method method); + + /** + * Read observer meta data from a existing {@link AnnotatedMethod} + * + * @param method to read meta data from + * @return self + */ + ObserverMethodConfigurator read(AnnotatedMethod method); + + /** + * Read observer meta data from a existing ObserverMethod + * + * @param method to read meta data from + * @return self + */ + ObserverMethodConfigurator read(ObserverMethod method); + + /** + * Set the class of the Bean containing this observer. If not set, the extension class is used. + * + * @param type the bean class containing this configurator. + * @return self + */ + ObserverMethodConfigurator beanClass(Class type); + + /** + * Set the type of the observed event + * + * @param type of the observed event + * @return self + */ + ObserverMethodConfigurator observedType(Type type); + + /** + * Add the qualifier to the observed event + * + * @param qualifier to add to event + * @return self + */ + ObserverMethodConfigurator addQualifier(Annotation qualifier); + + /** + * Add all the qualifiers to the Observed event + * + * @param qualifiers to add to event + * @return self + */ + ObserverMethodConfigurator addQualifiers(Annotation... qualifiers); + + /** + * Add all the qualifiers to the Observed event + * + * @param qualifiers to add to event + * @return self + */ + ObserverMethodConfigurator addQualifiers(Set qualifiers); + + /** + * Replace all qualifiers on the Observed event. + * + * @param qualifiers to put on event + * @return self + */ + ObserverMethodConfigurator qualifiers(Annotation... qualifiers); + + /** + * Replace all qualifiers on the Observed event. + * + * @param qualifiers to put on event + * @return self + */ + ObserverMethodConfigurator qualifiers(Set qualifiers); + + /** + * Set the {@link Reception} mode for the observer to build + * + * @param reception reception type + * @return self + */ + ObserverMethodConfigurator reception(Reception reception); + + /** + * Set the {@link TransactionPhase} for the observer to build + * + * @param transactionPhase phase for the observer + * @return self + */ + ObserverMethodConfigurator transactionPhase(TransactionPhase transactionPhase); + + /** + * Set the priority for the observer to build + * + * @param priority priority of the observer + * @return self + */ + ObserverMethodConfigurator priority(int priority); + + /** + * Define an operation that accepts a context of a fired event. + * + * @param callback to call for the event notification + * @return self + */ + ObserverMethodConfigurator notifyWith(EventConsumer callback); + + /** + * Allows modification of the asynchronous status of the observer to build. + * + * @param async async status + * @return self + */ + ObserverMethodConfigurator async(boolean async); + + /** + * Represents an operation that accepts a context of a fired event. + * + * @author Martin Kouba + * + * @param + * @see EventContext + */ + @FunctionalInterface + interface EventConsumer { + + /** + * + * @param eventContext the {@link EventContext} used to fire the event + * @throws Exception The thrown checked exception is wrapped and rethrown as an (unchecked) {@link ObserverException} + */ + void accept(EventContext eventContext) throws Exception; + + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/ProducerConfigurator.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/ProducerConfigurator.java new file mode 100644 index 000000000..d958ae2b0 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/configurator/ProducerConfigurator.java @@ -0,0 +1,62 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2016, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.inject.spi.configurator; + +import java.util.function.Consumer; +import java.util.function.Function; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.ProcessProducer; +import javax.enterprise.inject.spi.Producer; + +/** + *

+ * A {@link ProducerConfigurator} can configure a {@link Producer}. The container must provide an implementation of this + * interface. + *

+ * + *

+ * This configurator is not thread safe and shall not be used concurrently. + *

+ * + * @param The return type of the producer method or the type of the producer field + * @author Martin Kouba + * @see ProcessProducer#configureProducer() + * @since 2.0 + */ +public interface ProducerConfigurator { + + /** + * Set a callback to produce a new instance. + * + * @param callback a {@link Function} defining the callback to set + * @return self + * @see Producer#produce(CreationalContext) + */ + ProducerConfigurator produceWith(Function, U> callback); + + /** + * Set a callback to destroy the produced instance. + * + * @param callback a {@link Consumer} defining the callback to set + * @return self + * @see Producer#dispose(Object) + */ + ProducerConfigurator disposeWith(Consumer callback); + + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/package-info.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/package-info.java new file mode 100644 index 000000000..3ec655bb5 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/inject/spi/package-info.java @@ -0,0 +1,134 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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. + */ +/** + *

The portable extension integration SPI.

+ * + *

A portable extension may integrate with the container by:

+ * + *
    + *
  • Providing its own beans, interceptors and decorators to the + * container
  • + *
  • Injecting dependencies into its own objects using the + * dependency injection service
  • + *
  • Providing a context implementation for a custom scope
  • + *
  • Augmenting or overriding the annotation-based metadata with + * metadata from some other source
  • + *
+ * + *

The BeanManager object

+ * + *

Portable extensions sometimes interact directly with the container + * via programmatic API call. The interface + * {@link javax.enterprise.inject.spi.BeanManager} provides operations + * for obtaining contextual references for beans, along with many other + * operations of use to portable extensions.

+ * + *

Container lifecycle events

+ * + *

During the application initialization process, the container fires + * a series of {@linkplain javax.enterprise.event events}, allowing + * portable extensions to integrate with the container initialization + * process. Observer methods of these events must belong to + * {@linkplain javax.enterprise.inject.spi.Extension extensions} declared + * in META-INF/services.

+ * + *

Lifecycle events include + * {@link javax.enterprise.inject.spi.BeforeBeanDiscovery}, + * {@link javax.enterprise.inject.spi.AfterBeanDiscovery}, + * {@link javax.enterprise.inject.spi.AfterDeploymentValidation} and + * {@link javax.enterprise.inject.spi.BeforeShutdown}.

+ * + *

Interfaces representing enabled beans

+ * + *

The interfaces + * {@link javax.enterprise.inject.spi.Bean}, + * {@link javax.enterprise.inject.spi.Decorator}, + * {@link javax.enterprise.inject.spi.Interceptor} and + * {@link javax.enterprise.inject.spi.ObserverMethod} + * define everything the container needs to manage instances of + * a bean, interceptor, decorator or observer method.

+ * + *

An instance of Bean exists for every + * {@linkplain javax.enterprise.inject enabled bean}. A portable + * extension may add support for new kinds of beans by implementing + * Bean, observing the event + * {@link javax.enterprise.inject.spi.AfterBeanDiscovery} event + * {@linkplain javax.enterprise.inject.spi.AfterBeanDiscovery#addBean(Bean) + * registering beans} with the container. An instance of + * ObserverMethod exists for every + * {@linkplain javax.enterprise.event observer method} of every + * enabled bean. A portable extension may add observers by implementing + * ObserverMethod and + * {@linkplain javax.enterprise.inject.spi.AfterBeanDiscovery#addObserverMethod(ObserverMethod) + * registering an instance} with the container.

+ * + *

A portable extension may be notified of the existence of an + * enabled bean by observing the container lifecycle event type + * {@link javax.enterprise.inject.spi.ProcessBean} or one of its + * {@linkplain javax.enterprise.inject.spi.ProcessBean subtypes}, + * or of the existence of an observer method of an enabled bean by + * observing the event type + * {@link javax.enterprise.inject.spi.ProcessObserverMethod}.

+ * + *

Alternate metadata sources

+ * + *

A portable extension may provide an alternative metadata + * source, such as configuration by XML.

+ * + *

{@link javax.enterprise.inject.spi.Annotated} + * and its subtypes allow a portable extension to specify + * metadata that overrides the annotations that exist on a + * bean class. The portable extension is responsible for + * implementing the interfaces, thereby exposing the metadata + * to the container. The container must use the operations of + * Annotated and its subinterfaces to discover program + * element types and annotations, instead of directly calling the + * Java Reflection API.

+ * + *

A portable extension provides its metadata to the + * container by observing the event + * {@link javax.enterprise.inject.spi.ProcessAnnotatedType} and + * {@linkplain javax.enterprise.inject.spi.ProcessAnnotatedType#setAnnotatedType(AnnotatedType) + * wrapping} the {@link javax.enterprise.inject.spi.AnnotatedType}.

+ * + *

Producer and InjectionTarget

+ * + * The interfaces {@link javax.enterprise.inject.spi.Producer} and + * {@link javax.enterprise.inject.spi.InjectionTarget} abstract the + * basic lifecycle of (contextual or non-contextual) container managed + * objects, including instantiation and destruction, dependency injection + * and lifecycle callbacks.

+ * + *

An instance of {@link javax.enterprise.inject.spi.InjectionTarget} + * may be + * {@linkplain javax.enterprise.inject.spi.BeanManager#createInjectionTarget(AnnotatedType) + * obtained} from the {@link javax.enterprise.inject.spi.BeanManager}, + * allowing a portable extension to request these container services for + * objects under the control of the portable extension.

+ * + *

Furthermore, a portable extension may replace the implementation + * of {@link javax.enterprise.inject.spi.InjectionTarget} or + * {@link javax.enterprise.inject.spi.Producer} used by the container + * with its own implementation by observing the events + * {@link javax.enterprise.inject.spi.ProcessInjectionTarget} or + * {@link javax.enterprise.inject.spi.ProcessProducer}.

+ * + * @see javax.enterprise.inject + * @see javax.enterprise.context.spi + */ +package javax.enterprise.inject.spi; + diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/AnnotationLiteral.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/AnnotationLiteral.java new file mode 100644 index 000000000..7206bb1bd --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/AnnotationLiteral.java @@ -0,0 +1,295 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2018, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.util; + +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; + +/** + *

+ * Supports inline instantiation of annotation type instances. + *

+ * + *

+ * Reflection operations are using {@link SecurityActions} utility class to support security manager. + *

+ * + *

+ * An instance of an annotation type may be obtained by subclassing AnnotationLiteral. + *

+ * + *
+ * public abstract class PayByQualifier extends AnnotationLiteral<PayBy> implements PayBy {
+ * }
+ * 
+ * + *
+ * PayBy payByCheque = new PayByQualifier() {
+ *     public PaymentMethod value() {
+ *         return CHEQUE;
+ *     }
+ * };
+ * 
+ * + * @author Pete Muir + * @author Gavin King + * @author Marko Luksa + * @author Antoine Sabot-Durand + * + * @param the annotation type + * + * @see javax.enterprise.inject.Instance#select(Annotation...) + * @see javax.enterprise.event.Event#select(Annotation...) + * + */ +public abstract class AnnotationLiteral implements Annotation, Serializable { + + private static final long serialVersionUID = 1L; + + private transient Class annotationType; + private transient Method[] members; + private transient Integer cachedHashCode; + + protected AnnotationLiteral() { + if (getMembers().length == 0) { + this.cachedHashCode = 0; + } else { + this.cachedHashCode = null; + } + } + + private Method[] getMembers() { + if (members == null) { + members = SecurityActions.getDeclaredMethods(annotationType()); + if (members.length > 0 && !annotationType().isAssignableFrom(this.getClass())) { + throw new RuntimeException(getClass() + " does not implement the annotation type with members " + + annotationType().getName()); + } + } + return members; + } + + private static Class getAnnotationLiteralSubclass(Class clazz) { + Class superclass = clazz.getSuperclass(); + if (superclass.equals(AnnotationLiteral.class)) { + return clazz; + } else if (superclass.equals(Object.class)) { + return null; + } else { + return (getAnnotationLiteralSubclass(superclass)); + } + } + + @SuppressWarnings("unchecked") + private static Class getTypeParameter(Class annotationLiteralSuperclass) { + Type type = annotationLiteralSuperclass.getGenericSuperclass(); + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + if (parameterizedType.getActualTypeArguments().length == 1) { + return (Class) parameterizedType.getActualTypeArguments()[0]; + } + } + return null; + } + + public Class annotationType() { + if (annotationType == null) { + Class annotationLiteralSubclass = getAnnotationLiteralSubclass(this.getClass()); + if (annotationLiteralSubclass == null) { + throw new RuntimeException(getClass() + " is not a subclass of AnnotationLiteral"); + } + annotationType = getTypeParameter(annotationLiteralSubclass); + if (annotationType == null) { + throw new RuntimeException(getClass() + " does not specify the type parameter T of AnnotationLiteral"); + } + } + return annotationType; + } + + @Override + public String toString() { + StringBuilder string = new StringBuilder(); + string.append('@').append(annotationType().getName()).append('('); + for (int i = 0; i < getMembers().length; i++) { + string.append(getMembers()[i].getName()).append('='); + Object value = getMemberValue(getMembers()[i], this); + if (value instanceof boolean[]) { + appendInBraces(string, Arrays.toString((boolean[]) value)); + } else if (value instanceof byte[]) { + appendInBraces(string, Arrays.toString((byte[]) value)); + } else if (value instanceof short[]) { + appendInBraces(string, Arrays.toString((short[]) value)); + } else if (value instanceof int[]) { + appendInBraces(string, Arrays.toString((int[]) value)); + } else if (value instanceof long[]) { + appendInBraces(string, Arrays.toString((long[]) value)); + } else if (value instanceof float[]) { + appendInBraces(string, Arrays.toString((float[]) value)); + } else if (value instanceof double[]) { + appendInBraces(string, Arrays.toString((double[]) value)); + } else if (value instanceof char[]) { + appendInBraces(string, Arrays.toString((char[]) value)); + } else if (value instanceof String[]) { + String[] strings = (String[]) value; + String[] quoted = new String[strings.length]; + for (int j = 0; j < strings.length; j++) { + quoted[j] = "\"" + strings[j] + "\""; + } + appendInBraces(string, Arrays.toString(quoted)); + } else if (value instanceof Class[]) { + Class[] classes = (Class[]) value; + String[] names = new String[classes.length]; + for (int j = 0; j < classes.length; j++) { + names[j] = classes[j].getName() + ".class"; + } + appendInBraces(string, Arrays.toString(names)); + } else if (value instanceof Object[]) { + appendInBraces(string, Arrays.toString((Object[]) value)); + } else if (value instanceof String) { + string.append('"').append(value).append('"'); + } else if (value instanceof Class) { + string.append(((Class) value).getName()).append(".class"); + } else { + string.append(value); + } + if (i < getMembers().length - 1) { + string.append(", "); + } + } + return string.append(')').toString(); + } + + private void appendInBraces(StringBuilder buf, String s) { + buf.append('{').append(s.substring(1, s.length() - 1)).append('}'); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null) { + return false; + } + if (other instanceof Annotation) { + Annotation that = (Annotation) other; + if (this.annotationType().equals(that.annotationType())) { + for (Method member : getMembers()) { + Object thisValue = getMemberValue(member, this); + Object thatValue = getMemberValue(member, that); + if (thisValue instanceof byte[] && thatValue instanceof byte[]) { + if (!Arrays.equals((byte[]) thisValue, (byte[]) thatValue)) + return false; + } else if (thisValue instanceof short[] && thatValue instanceof short[]) { + if (!Arrays.equals((short[]) thisValue, (short[]) thatValue)) + return false; + } else if (thisValue instanceof int[] && thatValue instanceof int[]) { + if (!Arrays.equals((int[]) thisValue, (int[]) thatValue)) + return false; + } else if (thisValue instanceof long[] && thatValue instanceof long[]) { + if (!Arrays.equals((long[]) thisValue, (long[]) thatValue)) + return false; + } else if (thisValue instanceof float[] && thatValue instanceof float[]) { + if (!Arrays.equals((float[]) thisValue, (float[]) thatValue)) + return false; + } else if (thisValue instanceof double[] && thatValue instanceof double[]) { + if (!Arrays.equals((double[]) thisValue, (double[]) thatValue)) + return false; + } else if (thisValue instanceof char[] && thatValue instanceof char[]) { + if (!Arrays.equals((char[]) thisValue, (char[]) thatValue)) + return false; + } else if (thisValue instanceof boolean[] && thatValue instanceof boolean[]) { + if (!Arrays.equals((boolean[]) thisValue, (boolean[]) thatValue)) + return false; + } else if (thisValue instanceof Object[] && thatValue instanceof Object[]) { + if (!Arrays.equals((Object[]) thisValue, (Object[]) thatValue)) + return false; + } else { + if (!thisValue.equals(thatValue)) + return false; + } + } + return true; + } + } + return false; + } + + @Override + public int hashCode() { + if (cachedHashCode != null) { + return cachedHashCode; + } else { + int hashCode = 0; + for (Method member : getMembers()) { + int memberNameHashCode = 127 * member.getName().hashCode(); + Object value = getMemberValue(member, this); + int memberValueHashCode; + if (value instanceof boolean[]) { + memberValueHashCode = Arrays.hashCode((boolean[]) value); + } else if (value instanceof short[]) { + memberValueHashCode = Arrays.hashCode((short[]) value); + } else if (value instanceof int[]) { + memberValueHashCode = Arrays.hashCode((int[]) value); + } else if (value instanceof long[]) { + memberValueHashCode = Arrays.hashCode((long[]) value); + } else if (value instanceof float[]) { + memberValueHashCode = Arrays.hashCode((float[]) value); + } else if (value instanceof double[]) { + memberValueHashCode = Arrays.hashCode((double[]) value); + } else if (value instanceof byte[]) { + memberValueHashCode = Arrays.hashCode((byte[]) value); + } else if (value instanceof char[]) { + memberValueHashCode = Arrays.hashCode((char[]) value); + } else if (value instanceof Object[]) { + memberValueHashCode = Arrays.hashCode((Object[]) value); + } else { + memberValueHashCode = value.hashCode(); + } + hashCode += memberNameHashCode ^ memberValueHashCode; + } + return hashCode; + } + } + + private static Object getMemberValue(Method member, Annotation instance) { + Object value = invoke(member, instance); + if (value == null) { + throw new IllegalArgumentException("Annotation member value " + instance.getClass().getName() + "." + + member.getName() + " must not be null"); + } + return value; + } + + private static Object invoke(Method method, Object instance) { + try { + if (!method.isAccessible()) + SecurityActions.setAccessible(method); + return method.invoke(instance); + } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Error checking value of member method " + method.getName() + " on " + + method.getDeclaringClass(), e); + } + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/Nonbinding.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/Nonbinding.java new file mode 100644 index 000000000..72e8e0db9 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/Nonbinding.java @@ -0,0 +1,68 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.util; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + *

+ * Excludes a member of an annotation type (such as a {@linkplain javax.inject.Qualifier qualifier type} or + * {@linkplain javax.interceptor interceptor binding type}) from consideration when the container compares two annotation + * instances. + *

+ * + *
+ * @Qualifier
+ * @Retention(RUNTIME)
+ * @Target({ METHOD, FIELD, PARAMETER, TYPE })
+ * public @interface PayBy {
+ *     PaymentMethod value();
+ *
+ *     @Nonbinding
+ *     String comment();
+ * }
+ * 
+ * + * @author Gavin King + * + * @see javax.inject.Qualifier @Qualifier + * @see javax.interceptor.InterceptorBinding @InterceptorBinding + * + */ +@Retention(RUNTIME) +@Target(METHOD) +public @interface Nonbinding { + + /** + * Supports inline instantiation of the {@link Nonbinding} annotation. + * + * @author Martin Kouba + * @since 2.0 + */ + public static final class Literal extends AnnotationLiteral implements Nonbinding { + + public static final Literal INSTANCE = new Literal(); + + private static final long serialVersionUID = 1L; + + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/SecurityActions.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/SecurityActions.java new file mode 100644 index 000000000..055b7960e --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/SecurityActions.java @@ -0,0 +1,60 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2018, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * Licensed under the Apache License, Version 2.1-SNAPSHOT (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.1-SNAPSHOT + * 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 javax.enterprise.util; + +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * + * This utility class is used to optimize invocation made through the SecurityManager + * + * @author Antoine Sabot-durand + */ + +final class SecurityActions { + + private SecurityActions() { + + } + + static void setAccessible(Method method) { + if (System.getSecurityManager() != null) { + AccessController.doPrivileged( + (PrivilegedAction) () -> { + method.setAccessible(true); + return null; + } + ); + } else { + method.setAccessible(true); + } + } + + + static Method[] getDeclaredMethods(Class clazz) { + if (System.getSecurityManager() != null) { + return AccessController.doPrivileged( + (PrivilegedAction) () -> clazz.getDeclaredMethods() + ); + } else { + return clazz.getDeclaredMethods(); + } + } +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/TypeLiteral.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/TypeLiteral.java new file mode 100644 index 000000000..2d4abff7b --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/TypeLiteral.java @@ -0,0 +1,133 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 javax.enterprise.util; + +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + *

+ * Supports inline instantiation of objects that represent parameterized types with actual type parameters. + *

+ * + *

+ * An object that represents any parameterized type may be obtained by subclassing TypeLiteral. + *

+ * + *
+ * TypeLiteral<List<String>> stringListType = new TypeLiteral<List<String>>() {
+ * };
+ * 
+ * + * @author Gavin King + * @author Pete Muir + * + * @param the type, including all actual type parameters + * + * @see javax.enterprise.inject.Instance#select(TypeLiteral, Annotation...) + * @see javax.enterprise.event.Event#select(TypeLiteral, Annotation...) + * + */ +public abstract class TypeLiteral implements Serializable { + + private static final long serialVersionUID = 1L; + + private transient Type actualType; + + protected TypeLiteral() { + } + + /** + * @return the actual type represented by this object + */ + public final Type getType() { + if (actualType == null) { + Class typeLiteralSubclass = getTypeLiteralSubclass(this.getClass()); + if (typeLiteralSubclass == null) { + throw new RuntimeException(getClass() + " is not a subclass of TypeLiteral"); + } + actualType = getTypeParameter(typeLiteralSubclass); + if (actualType == null) { + throw new RuntimeException(getClass() + " does not specify the type parameter T of TypeLiteral"); + } + } + return actualType; + } + + /** + * @return the raw type represented by this object + */ + @SuppressWarnings("unchecked") + public final Class getRawType() { + Type type = getType(); + if (type instanceof Class) { + return (Class) type; + } else if (type instanceof ParameterizedType) { + return (Class) ((ParameterizedType) type).getRawType(); + } else if (type instanceof GenericArrayType) { + return (Class) Object[].class; + } else { + throw new RuntimeException("Illegal type"); + } + } + + private static Class getTypeLiteralSubclass(Class clazz) { + Class superclass = clazz.getSuperclass(); + if (superclass.equals(TypeLiteral.class)) { + return clazz; + } else if (superclass.equals(Object.class)) { + return null; + } else { + return (getTypeLiteralSubclass(superclass)); + } + } + + private static Type getTypeParameter(Class superclass) { + Type type = superclass.getGenericSuperclass(); + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + if (parameterizedType.getActualTypeArguments().length == 1) { + return parameterizedType.getActualTypeArguments()[0]; + } + } + return null; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TypeLiteral) { + TypeLiteral that = (TypeLiteral) obj; + return this.getType().equals(that.getType()); + } + return false; + } + + @Override + public int hashCode() { + return getType().hashCode(); + } + + @Override + public String toString() { + return getType().toString(); + } + +} diff --git a/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/package-info.java b/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/package-info.java new file mode 100644 index 000000000..d69948cd6 --- /dev/null +++ b/fine-third-default/fine-javax-cdi/src/javax/enterprise/util/package-info.java @@ -0,0 +1,21 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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. + */ + +/** + * Contains shared, general-purpose helper classes and annotations. + */ +package javax.enterprise.util; diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/ExpressionFactoryImpl.java b/fine-third-default/fine-javax-el/src/com/sun/el/ExpressionFactoryImpl.java new file mode 100644 index 000000000..a225d24f9 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/ExpressionFactoryImpl.java @@ -0,0 +1,137 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el; + +import java.util.Map; +import java.util.HashMap; +import java.lang.reflect.Method; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.ExpressionFactory; +import javax.el.MethodExpression; +import javax.el.ValueExpression; +import javax.el.ELResolver; + +import com.sun.el.lang.ExpressionBuilder; +import com.sun.el.lang.ELSupport; +import com.sun.el.util.MessageFactory; +import com.sun.el.stream.StreamELResolver; + +/** + * @see javax.el.ExpressionFactory + * + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class ExpressionFactoryImpl extends ExpressionFactory { + + /** + * + */ + public ExpressionFactoryImpl() { + super(); + } + + public Object coerceToType(Object obj, Class type) { + Object ret; + try { + ret = ELSupport.coerceToType(obj, type); + } catch (IllegalArgumentException ex) { + throw new ELException(ex); + } + return ret; + } + + public MethodExpression createMethodExpression(ELContext context, + String expression, Class expectedReturnType, + Class[] expectedParamTypes) { + ExpressionBuilder builder = new ExpressionBuilder(expression, context); + MethodExpression me = builder.createMethodExpression(expectedReturnType, + expectedParamTypes); + if (expectedParamTypes == null && !me.isParametersProvided()) { + throw new NullPointerException(MessageFactory + .get("error.method.nullParms")); + } + return me; + } + + public ValueExpression createValueExpression(ELContext context, + String expression, Class expectedType) { + if (expectedType == null) { + throw new NullPointerException(MessageFactory + .get("error.value.expectedType")); + } + ExpressionBuilder builder = new ExpressionBuilder(expression, context); + return builder.createValueExpression(expectedType); + } + + public ValueExpression createValueExpression(Object instance, + Class expectedType) { + if (expectedType == null) { + throw new NullPointerException(MessageFactory + .get("error.value.expectedType")); + } + return new ValueExpressionLiteral(instance, expectedType); + } + + @Override + public ELResolver getStreamELResolver() { + return new StreamELResolver(); + } + + @Override + public Map getInitFunctionMap() { + Map funcs = new HashMap(); +/* + Class genClass = Generation.class; + try { + funcs.put("collections:range", genClass.getMethod("range", + new Class[] {Integer.TYPE, Integer.TYPE})); + funcs.put("collections:repeat", genClass.getMethod("repeat", + new Class[] {Object.class, Integer.TYPE})); + } catch (NoSuchMethodException ex) { + // Should not happen + } +*/ + return funcs; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/Messages.properties b/fine-third-default/fine-javax-el/src/com/sun/el/Messages.properties new file mode 100644 index 000000000..34c346c5e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/Messages.properties @@ -0,0 +1,102 @@ +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common Development +# and Distribution License("CDDL") (collectively, the "License"). You +# may not use this file except in compliance with the License. You can +# obtain a copy of the License at +# https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html +# or packager/legal/LICENSE.txt. See the License for the specific +# language governing permissions and limitations under the License. +# +# When distributing the software, include this License Header Notice in each +# file and include the License file at packager/legal/LICENSE.txt. +# +# GPL Classpath Exception: +# Oracle designates this particular file as subject to the "Classpath" +# exception as provided by Oracle in the GPL Version 2 section of the License +# file that accompanied this code. +# +# Modifications: +# If applicable, add the following below the License Header, with the fields +# enclosed by brackets [] replaced by your own identifying information: +# "Portions Copyright [year] [name of copyright owner]" +# +# Contributor(s): +# If you wish your version of this file to be governed by only the CDDL or +# only the GPL Version 2, indicate your decision by adding "[Contributor] +# elects to include this software in this distribution under the [CDDL or GPL +# Version 2] license." If you don't indicate a single choice of license, a +# recipient has the option to distribute your version of this file under +# either the CDDL, the GPL Version 2 or to extend the choice of license to +# its licensees as provided above. However, if you add GPL Version 2 code +# and therefore, elected the GPL Version 2 license, then the option applies +# only if the new code is made subject to such option by the copyright +# holder. +# + +# General Errors +error.convert=Cannot convert {0} of type {1} to {2} +error.compare=Cannot compare {0} to {1} +error.function=Problems calling function ''{0}'' +error.function.syntax=Syntax error in calling function ''{0}'' +error.unreachable.base=Target Unreachable, identifier ''{0}'' resolved to null +error.unreachable.property=Target Unreachable, ''{0}'' returned null +error.resolver.unhandled=ELResolver did not handle type: {0} with property of ''{1}'' +error.resolver.unhandled.null=ELResolver cannot handle a null base Object with identifier ''{0}'' + +# ValueExpressionLiteral +error.value.literal.write=ValueExpression is a literal and not writable: {0} + +# ExpressionFactoryImpl +error.null=Expression cannot be null +error.mixed=Expression cannot contain both '#{..}' and '${..}' : {0} +error.method=Not a valid MethodExpression : {0} +error.method.nullParms=Parameter types cannot be null +error.value.expectedType=Expected type cannot be null + +# ExpressionMediator +error.eval=Error Evaluating {0} : {1} + +# ValueSetVisitor +error.syntax.set=Illegal Syntax for Set Operation + +error.syntax.assign=Illegal Syntax for Assign Operation + +# ReflectionUtil +error.method.notfound=Method not found: {0}.{1}({2}) +error.property.notfound=Property ''{1}'' not found on {0} + +# ValidatingVisitor +error.fnMapper.null=Expression uses functions, but no FunctionMapper was provided +error.fnMapper.method=Function ''{0}'' not found +error.fnMapper.paramcount=Function ''{0}'' specifies {1} params, but {2} were supplied + +# **ExpressionImpl +error.context.null=ELContext was null + +# ArrayELResolver +error.array.outofbounds=Index {0} is out of bounds for array of size {1} + +# ListELResolver +error.list.outofbounds=Index {0} is out of bounds for list of size {1} + +# BeanELResolver +error.property.notfound=Property ''{1}'' not found on type: {0} +error.property.invocation=Property ''{1}'' threw an exception from type: {0} +error.property.notreadable=Property ''{1}'' doesn't have a 'get' specified on type: {0} +error.property.notwritable=Property ''{1}'' doesn't have a 'set' specified on type: {0} + +# AstValue +error.method.name=An instance of {0} is specified as the static method name, it +must be a String + +# AstType +error.class.notfound=The specified class ''{0}'' not found + +# AstLambdaExpression +error.lambda.call=A Lambda expression must return another Lambda expression in this syntax +error.lambda.parameter.readonly=The Lambda parameter ''{0}'' is not writable diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/MethodExpressionImpl.java b/fine-third-default/fine-javax-el/src/com/sun/el/MethodExpressionImpl.java new file mode 100644 index 000000000..940b6346c --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/MethodExpressionImpl.java @@ -0,0 +1,350 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.ELResolver; +import javax.el.Expression; +import javax.el.ExpressionFactory; +import javax.el.FunctionMapper; +import javax.el.MethodExpression; +import javax.el.MethodInfo; +import javax.el.MethodNotFoundException; +import javax.el.PropertyNotFoundException; +import javax.el.VariableMapper; +import javax.el.EvaluationListener; + +import com.sun.el.lang.ELSupport; +import com.sun.el.lang.EvaluationContext; +import com.sun.el.lang.ExpressionBuilder; +import com.sun.el.parser.Node; +import com.sun.el.util.ReflectionUtil; + +/** + * An Expression that refers to a method on an object. + * + *

+ * The {@link ExpressionFactory#createMethodExpression} method + * can be used to parse an expression string and return a concrete instance + * of MethodExpression that encapsulates the parsed expression. + * The {@link FunctionMapper} is used at parse time, not evaluation time, + * so one is not needed to evaluate an expression using this class. + * However, the {@link ELContext} is needed at evaluation time.

+ * + *

The {@link #getMethodInfo} and {@link #invoke} methods will evaluate the + * expression each time they are called. The {@link ELResolver} in the + * ELContext is used to resolve the top-level variables and to + * determine the behavior of the . and [] + * operators. For any of the two methods, the {@link ELResolver#getValue} + * method is used to resolve all properties up to but excluding the last + * one. This provides the base object on which the method + * appears. If the base object is null, a + * NullPointerException must be thrown. At the last resolution, + * the final property is then coerced to a String, + * which provides the name of the method to be found. A method matching the + * name and expected parameters provided at parse time is found and it is + * either queried or invoked (depending on the method called on this + * MethodExpression).

+ * + *

See the notes about comparison, serialization and immutability in + * the {@link Expression} javadocs. + * + * @see javax.el.ELResolver + * @see javax.el.Expression + * @see javax.el.ExpressionFactory + * @see javax.el.MethodExpression + * + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class MethodExpressionImpl extends MethodExpression implements + Externalizable { + + private Class expectedType; + + private String expr; + + private FunctionMapper fnMapper; + + private VariableMapper varMapper; + + private transient Node node; + + private Class[] paramTypes; + + /** + * + */ + public MethodExpressionImpl() { + super(); + } + + /** + * @param expr + * @param node + * @param fnMapper + * @param expectedType + * @param paramTypes + */ + public MethodExpressionImpl(String expr, Node node, + FunctionMapper fnMapper, VariableMapper varMapper, + Class expectedType, Class[] paramTypes) { + super(); + this.expr = expr; + this.node = node; + this.fnMapper = fnMapper; + this.varMapper = varMapper; + this.expectedType = expectedType; + this.paramTypes = paramTypes; + } + + /** + * Determines whether the specified object is equal to this + * Expression. + * + *

+ * The result is true if and only if the argument is not + * null, is an Expression object that is the + * of the same type (ValueExpression or + * MethodExpression), and has an identical parsed + * representation. + *

+ * + *

+ * Note that two expressions can be equal if their expression Strings are + * different. For example, ${fn1:foo()} and + * ${fn2:foo()} are equal if their corresponding + * FunctionMappers mapped fn1:foo and + * fn2:foo to the same method. + *

+ * + * @param obj + * the Object to test for equality. + * @return true if obj equals this + * Expression; false otherwise. + * @see java.util.Hashtable + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj instanceof MethodExpressionImpl) { + MethodExpressionImpl me = (MethodExpressionImpl) obj; + return getNode().equals(me.getNode()); + } + return false; + } + + /** + * Returns the original String used to create this Expression, + * unmodified. + * + *

+ * This is used for debugging purposes but also for the purposes of + * comparison (e.g. to ensure the expression in a configuration file has not + * changed). + *

+ * + *

+ * This method does not provide sufficient information to re-create an + * expression. Two different expressions can have exactly the same + * expression string but different function mappings. Serialization should + * be used to save and restore the state of an Expression. + *

+ * + * @return The original expression String. + * + * @see javax.el.Expression#getExpressionString() + */ + public String getExpressionString() { + return this.expr; + } + + /** + * Evaluates the expression relative to the provided context, and returns + * information about the actual referenced method. + * + * @param context + * The context of this evaluation + * @return an instance of MethodInfo containing information + * about the method the expression evaluated to. + * @throws NullPointerException + * if context is null or the base object is + * null on the last resolution. + * @throws PropertyNotFoundException + * if one of the property resolutions failed because a specified + * variable or property does not exist or is not readable. + * @throws MethodNotFoundException + * if no suitable method can be found. + * @throws ELException + * if an exception was thrown while performing property or + * variable resolution. The thrown exception must be included as + * the cause property of this exception, if available. + * @see javax.el.MethodExpression#getMethodInfo(javax.el.ELContext) + */ + public MethodInfo getMethodInfo(ELContext context) + throws PropertyNotFoundException, MethodNotFoundException, + ELException { + Node n = this.getNode(); + EvaluationContext ctx = new EvaluationContext(context, this.fnMapper, + this.varMapper); + return n.getMethodInfo(ctx, this.paramTypes); + } + + /** + * @return The Node for the expression + * @throws ELException + */ + private Node getNode() throws ELException { + if (this.node == null) { + this.node = ExpressionBuilder.createNode(this.expr); + } + return this.node; + } + + /** + * Returns the hash code for this Expression. + * + *

+ * See the note in the {@link #equals} method on how two expressions can be + * equal if their expression Strings are different. Recall that if two + * objects are equal according to the equals(Object) method, + * then calling the hashCode method on each of the two + * objects must produce the same integer result. Implementations must take + * special note and implement hashCode correctly. + *

+ * + * @return The hash code for this Expression. + * @see #equals + * @see java.util.Hashtable + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return getNode().hashCode(); + } + + /** + * Evaluates the expression relative to the provided context, invokes the + * method that was found using the supplied parameters, and returns the + * result of the method invocation. + * + * @param context + * The context of this evaluation. + * @param params + * The parameters to pass to the method, or null + * if no parameters. + * @return the result of the method invocation (null if the + * method has a void return type). + * @throws NullPointerException + * if context is null or the base object is + * null on the last resolution. + * @throws PropertyNotFoundException + * if one of the property resolutions failed because a specified + * variable or property does not exist or is not readable. + * @throws MethodNotFoundException + * if no suitable method can be found. + * @throws ELException + * if an exception was thrown while performing property or + * variable resolution. The thrown exception must be included as + * the cause property of this exception, if available. If the + * exception thrown is an InvocationTargetException, + * extract its cause and pass it to the + * ELException constructor. + * @see javax.el.MethodExpression#invoke(javax.el.ELContext, + * java.lang.Object[]) + */ + public Object invoke(ELContext context, Object[] params) + throws PropertyNotFoundException, MethodNotFoundException, + ELException { + EvaluationContext ctx = new EvaluationContext(context, this.fnMapper, + this.varMapper); + ctx.notifyBeforeEvaluation(this.expr); + Object obj = this.getNode().invoke(ctx, this.paramTypes, params); + ctx.notifyAfterEvaluation(this.expr); + return obj; + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#readExternal(java.io.ObjectInput) + */ + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + this.expr = in.readUTF(); + String type = in.readUTF(); + if (!"".equals(type)) { + this.expectedType = ReflectionUtil.forName(type); + } + this.paramTypes = ReflectionUtil.toTypeArray(((String[]) in + .readObject())); + this.fnMapper = (FunctionMapper) in.readObject(); + this.varMapper = (VariableMapper) in.readObject(); + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) + */ + public void writeExternal(ObjectOutput out) throws IOException { + out.writeUTF(this.expr); + out.writeUTF((this.expectedType != null) ? this.expectedType.getName() + : ""); + out.writeObject(ReflectionUtil.toTypeNameArray(this.paramTypes)); + out.writeObject(this.fnMapper); + out.writeObject(this.varMapper); + } + + public boolean isLiteralText() { + return false; + } + + @Override + public boolean isParametersProvided() { + return this.getNode().isParametersProvided(); + } +} + diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/MethodExpressionLiteral.java b/fine-third-default/fine-javax-el/src/com/sun/el/MethodExpressionLiteral.java new file mode 100644 index 000000000..d13de92fb --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/MethodExpressionLiteral.java @@ -0,0 +1,122 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.MethodExpression; +import javax.el.MethodInfo; + +import com.sun.el.lang.ELSupport; +import com.sun.el.util.ReflectionUtil; + +public class MethodExpressionLiteral extends MethodExpression implements Externalizable { + + private Class expectedType; + + private String expr; + + private Class[] paramTypes; + + public MethodExpressionLiteral() { + // do nothing + } + + public MethodExpressionLiteral(String expr, Class expectedType, Class[] paramTypes) { + this.expr = expr; + this.expectedType = expectedType; + this.paramTypes = paramTypes; + } + + public MethodInfo getMethodInfo(ELContext context) throws ELException { + return new MethodInfo(this.expr, this.expectedType, this.paramTypes); + } + + public Object invoke(ELContext context, Object[] params) throws ELException { + if (this.expectedType == null) { + return this.expr; + } + + try { + return context.convertToType(this.expr, this.expectedType); + } catch (Exception ex) { + throw new ELException (ex); + } + } + + public String getExpressionString() { + return this.expr; + } + + public boolean equals(Object obj) { + return (obj instanceof MethodExpressionLiteral && this.hashCode() == obj.hashCode()); + } + + public int hashCode() { + return this.expr.hashCode(); + } + + public boolean isLiteralText() { + return true; + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + this.expr = in.readUTF(); + String type = in.readUTF(); + if (!"".equals(type)) { + this.expectedType = ReflectionUtil.forName(type); + } + this.paramTypes = ReflectionUtil.toTypeArray(((String[]) in + .readObject())); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeUTF(this.expr); + out.writeUTF((this.expectedType != null) ? this.expectedType.getName() + : ""); + out.writeObject(ReflectionUtil.toTypeNameArray(this.paramTypes)); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/ValueExpressionImpl.java b/fine-third-default/fine-javax-el/src/com/sun/el/ValueExpressionImpl.java new file mode 100644 index 000000000..eb1df5af0 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/ValueExpressionImpl.java @@ -0,0 +1,308 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.ELResolver; +import javax.el.Expression; +import javax.el.ExpressionFactory; +import javax.el.FunctionMapper; +import javax.el.PropertyNotFoundException; +import javax.el.PropertyNotWritableException; +import javax.el.ValueExpression; +import javax.el.VariableMapper; +import javax.el.ValueReference; +import javax.el.EvaluationListener; + +import com.sun.el.lang.ELSupport; +import com.sun.el.lang.EvaluationContext; +import com.sun.el.lang.ExpressionBuilder; +import com.sun.el.parser.AstLiteralExpression; +import com.sun.el.parser.Node; +import com.sun.el.util.ReflectionUtil; + +/** + * An Expression that can get or set a value. + * + *

+ * In previous incarnations of this API, expressions could only be read. + * ValueExpression objects can now be used both to retrieve a + * value and to set a value. Expressions that can have a value set on them are + * referred to as l-value expressions. Those that cannot are referred to as + * r-value expressions. Not all r-value expressions can be used as l-value + * expressions (e.g. "${1+1}" or + * "${firstName} ${lastName}"). See the EL Specification for + * details. Expressions that cannot be used as l-values must always return + * true from isReadOnly(). + *

+ * + *

+ * The {@link ExpressionFactory#createValueExpression} method + * can be used to parse an expression string and return a concrete instance + * of ValueExpression that encapsulates the parsed expression. + * The {@link FunctionMapper} is used at parse time, not evaluation time, + * so one is not needed to evaluate an expression using this class. + * However, the {@link ELContext} is needed at evaluation time.

+ * + *

The {@link #getValue}, {@link #setValue}, {@link #isReadOnly} and + * {@link #getType} methods will evaluate the expression each time they are + * called. The {@link ELResolver} in the ELContext is used to + * resolve the top-level variables and to determine the behavior of the + * . and [] operators. For any of the four methods, + * the {@link ELResolver#getValue} method is used to resolve all properties + * up to but excluding the last one. This provides the base + * object. At the last resolution, the ValueExpression will + * call the corresponding {@link ELResolver#getValue}, + * {@link ELResolver#setValue}, {@link ELResolver#isReadOnly} or + * {@link ELResolver#getType} method, depending on which was called on + * the ValueExpression. + *

+ * + *

See the notes about comparison, serialization and immutability in + * the {@link Expression} javadocs. + * + * @see javax.el.ELResolver + * @see javax.el.Expression + * @see javax.el.ExpressionFactory + * @see javax.el.ValueExpression + * + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dochez $ + */ +public final class ValueExpressionImpl extends ValueExpression implements + Externalizable { + + private Class expectedType; + + private String expr; + + private FunctionMapper fnMapper; + + private VariableMapper varMapper; + + private transient Node node; + + public ValueExpressionImpl() { + + } + + /** + * + */ + public ValueExpressionImpl(String expr, Node node, FunctionMapper fnMapper, + VariableMapper varMapper, Class expectedType) { + this.expr = expr; + this.node = node; + this.fnMapper = fnMapper; + this.varMapper = varMapper; + this.expectedType = expectedType; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj instanceof ValueExpressionImpl) { + ValueExpressionImpl v = (ValueExpressionImpl) obj; + return getNode().equals(v.getNode()); + } + return false; + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#getExpectedType() + */ + public Class getExpectedType() { + return this.expectedType; + } + + /** + * Returns the type the result of the expression will be coerced to after + * evaluation. + * + * @return the expectedType passed to the + * ExpressionFactory.createValueExpression method + * that created this ValueExpression. + * + * @see javax.el.Expression#getExpressionString() + */ + public String getExpressionString() { + return this.expr; + } + + /** + * @return The Node for the expression + * @throws ELException + */ + private Node getNode() throws ELException { + if (this.node == null) { + this.node = ExpressionBuilder.createNode(this.expr); + } + return this.node; + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#getType(javax.el.ELContext) + */ + public Class getType(ELContext context) throws PropertyNotFoundException, + ELException { + EvaluationContext ctx = new EvaluationContext(context, this.fnMapper, + this.varMapper); + return this.getNode().getType(ctx); + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#getValueReference(javax.el.ELContext) + */ + public ValueReference getValueReference(ELContext context) + throws PropertyNotFoundException, ELException { + EvaluationContext ctx = new EvaluationContext(context, this.fnMapper, + this.varMapper); + return this.getNode().getValueReference(ctx); + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#getValue(javax.el.ELContext) + */ + public Object getValue(ELContext context) throws PropertyNotFoundException, + ELException { + EvaluationContext ctx = new EvaluationContext(context, this.fnMapper, + this.varMapper); + ctx.notifyBeforeEvaluation(this.expr); + Object value = this.getNode().getValue(ctx); + if (this.expectedType != null) { + try { + value = context.convertToType(value, this.expectedType); + } catch (IllegalArgumentException ex) { + throw new ELException(ex); + } + } + ctx.notifyAfterEvaluation(this.expr); + return value; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return getNode().hashCode(); + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#isLiteralText() + */ + public boolean isLiteralText() { + try { + return this.getNode() instanceof AstLiteralExpression; + } catch (ELException ele) { + return false; + } + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#isReadOnly(javax.el.ELContext) + */ + public boolean isReadOnly(ELContext context) + throws PropertyNotFoundException, ELException { + EvaluationContext ctx = new EvaluationContext(context, this.fnMapper, + this.varMapper); + return this.getNode().isReadOnly(ctx); + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + this.expr = in.readUTF(); + String type = in.readUTF(); + if (!"".equals(type)) { + this.expectedType = ReflectionUtil.forName(type); + } + this.fnMapper = (FunctionMapper) in.readObject(); + this.varMapper = (VariableMapper) in.readObject(); + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#setValue(javax.el.ELContext, + * java.lang.Object) + */ + public void setValue(ELContext context, Object value) + throws PropertyNotFoundException, PropertyNotWritableException, + ELException { + EvaluationContext ctx = new EvaluationContext(context, this.fnMapper, + this.varMapper); + this.getNode().setValue(ctx, value); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeUTF(this.expr); + out.writeUTF((this.expectedType != null) ? this.expectedType.getName() + : ""); + out.writeObject(this.fnMapper); + out.writeObject(this.varMapper); + } + + public String toString() { + return "ValueExpression["+this.expr+"]"; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/ValueExpressionLiteral.java b/fine-third-default/fine-javax-el/src/com/sun/el/ValueExpressionLiteral.java new file mode 100644 index 000000000..ddea0b50e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/ValueExpressionLiteral.java @@ -0,0 +1,140 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el; + +import java.io.Externalizable; +import java.io.IOException; +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.PropertyNotWritableException; + +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.el.ValueExpression; + +import com.sun.el.lang.ELSupport; +import com.sun.el.util.MessageFactory; +import com.sun.el.util.ReflectionUtil; + +public final class ValueExpressionLiteral extends ValueExpression implements + Externalizable { + + private static final long serialVersionUID = 1L; + + private Object value; + + private Class expectedType; + + public ValueExpressionLiteral() { + super(); + } + + public ValueExpressionLiteral(Object value, Class expectedType) { + this.value = value; + this.expectedType = expectedType; + } + + public Object getValue(ELContext context) { + if (this.expectedType != null) { + try { + return context.convertToType(this.value, this.expectedType); + } catch (IllegalArgumentException ex) { + throw new ELException(ex); + } + } + return this.value; + } + + public void setValue(ELContext context, Object value) { + throw new PropertyNotWritableException(MessageFactory.get( + "error.value.literal.write", this.value)); + } + + public boolean isReadOnly(ELContext context) { + return true; + } + + public Class getType(ELContext context) { + return (this.value != null) ? this.value.getClass() : null; + } + + public Class getExpectedType() { + return this.expectedType; + } + + public String getExpressionString() { + return (this.value != null) ? this.value.toString() : null; + } + + public boolean equals(Object obj) { + return (obj instanceof ValueExpressionLiteral && this + .equals((ValueExpressionLiteral) obj)); + } + + public boolean equals(ValueExpressionLiteral ve) { + return (ve != null && (this.value != null && ve.value != null && (this.value == ve.value || this.value + .equals(ve.value)))); + } + + public int hashCode() { + return (this.value != null) ? this.value.hashCode() : 0; + } + + public boolean isLiteralText() { + return true; + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(this.value); + out.writeUTF((this.expectedType != null) ? this.expectedType.getName() + : ""); + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + this.value = in.readObject(); + String type = in.readUTF(); + if (!"".equals(type)) { + this.expectedType = ReflectionUtil.forName(type); + } + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/lang/ELArithmetic.java b/fine-third-default/fine-javax-el/src/com/sun/el/lang/ELArithmetic.java new file mode 100644 index 000000000..b72585c7a --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/lang/ELArithmetic.java @@ -0,0 +1,399 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.lang; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.sun.el.util.MessageFactory; + +/** + * A helper class of Arithmetic defined by the EL Specification + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public abstract class ELArithmetic { + + public final static class BigDecimalDelegate extends ELArithmetic { + + protected Number add(Number num0, Number num1) { + return ((BigDecimal) num0).add((BigDecimal) num1); + } + + protected Number coerce(Number num) { + if (num instanceof BigDecimal) + return num; + if (num instanceof BigInteger) + return new BigDecimal((BigInteger) num); + return new BigDecimal(num.doubleValue()); + } + + protected Number coerce(String str) { + return new BigDecimal(str); + } + + protected Number divide(Number num0, Number num1) { + return ((BigDecimal) num0).divide((BigDecimal) num1, + BigDecimal.ROUND_HALF_UP); + } + + protected Number subtract(Number num0, Number num1) { + return ((BigDecimal) num0).subtract((BigDecimal) num1); + } + + protected Number mod(Number num0, Number num1) { + return Double.valueOf(num0.doubleValue() % num1.doubleValue()); + } + + protected Number multiply(Number num0, Number num1) { + return ((BigDecimal) num0).multiply((BigDecimal) num1); + } + + public boolean matches(Object obj0, Object obj1) { + return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal); + } + } + + public final static class BigIntegerDelegate extends ELArithmetic { + + protected Number add(Number num0, Number num1) { + return ((BigInteger) num0).add((BigInteger) num1); + } + + protected Number coerce(Number num) { + if (num instanceof BigInteger) + return num; + return new BigInteger(num.toString()); + } + + protected Number coerce(String str) { + return new BigInteger(str); + } + + protected Number divide(Number num0, Number num1) { + return (new BigDecimal((BigInteger) num0)).divide(new BigDecimal((BigInteger) num1), BigDecimal.ROUND_HALF_UP); + } + + protected Number multiply(Number num0, Number num1) { + return ((BigInteger) num0).multiply((BigInteger) num1); + } + + protected Number mod(Number num0, Number num1) { + return ((BigInteger) num0).mod((BigInteger) num1); + } + + protected Number subtract(Number num0, Number num1) { + return ((BigInteger) num0).subtract((BigInteger) num1); + } + + public boolean matches(Object obj0, Object obj1) { + return (obj0 instanceof BigInteger || obj1 instanceof BigInteger); + } + } + + public final static class DoubleDelegate extends ELArithmetic { + + protected Number add(Number num0, Number num1) { + // could only be one of these + if (num0 instanceof BigDecimal) { + return ((BigDecimal) num0).add(new BigDecimal(num1.doubleValue())); + } else if (num1 instanceof BigDecimal) { + return ((new BigDecimal(num0.doubleValue()).add((BigDecimal) num1))); + } + return Double.valueOf(num0.doubleValue() + num1.doubleValue()); + } + + protected Number coerce(Number num) { + if (num instanceof Double) + return num; + if (num instanceof BigInteger) + return new BigDecimal((BigInteger) num); + return Double.valueOf(num.doubleValue()); + } + + protected Number coerce(String str) { + return Double.valueOf(str); + } + + protected Number divide(Number num0, Number num1) { + return Double.valueOf(num0.doubleValue() / num1.doubleValue()); + } + + protected Number mod(Number num0, Number num1) { + return Double.valueOf(num0.doubleValue() % num1.doubleValue()); + } + + protected Number subtract(Number num0, Number num1) { + // could only be one of these + if (num0 instanceof BigDecimal) { + return ((BigDecimal) num0).subtract(new BigDecimal(num1.doubleValue())); + } else if (num1 instanceof BigDecimal) { + return ((new BigDecimal(num0.doubleValue()).subtract((BigDecimal) num1))); + } + return Double.valueOf(num0.doubleValue() - num1.doubleValue()); + } + + protected Number multiply(Number num0, Number num1) { + // could only be one of these + if (num0 instanceof BigDecimal) { + return ((BigDecimal) num0).multiply(new BigDecimal(num1.doubleValue())); + } else if (num1 instanceof BigDecimal) { + return ((new BigDecimal(num0.doubleValue()).multiply((BigDecimal) num1))); + } + return Double.valueOf(num0.doubleValue() * num1.doubleValue()); + } + + public boolean matches(Object obj0, Object obj1) { + return (obj0 instanceof Double + || obj1 instanceof Double + || obj0 instanceof Float + || obj1 instanceof Float + || (obj0 != null && (Double.TYPE == obj0.getClass() || Float.TYPE == obj0.getClass())) + || (obj1 != null && (Double.TYPE == obj1.getClass() || Float.TYPE == obj1.getClass())) + || (obj0 instanceof String && ELSupport + .isStringFloat((String) obj0)) || (obj1 instanceof String && ELSupport + .isStringFloat((String) obj1))); + } + } + + public final static class LongDelegate extends ELArithmetic { + + protected Number add(Number num0, Number num1) { + return Long.valueOf(num0.longValue() + num1.longValue()); + } + + protected Number coerce(Number num) { + if (num instanceof Long) + return num; + return Long.valueOf(num.longValue()); + } + + protected Number coerce(String str) { + return Long.valueOf(str); + } + + protected Number divide(Number num0, Number num1) { + return Long.valueOf(num0.longValue() / num1.longValue()); + } + + protected Number mod(Number num0, Number num1) { + return Long.valueOf(num0.longValue() % num1.longValue()); + } + + protected Number subtract(Number num0, Number num1) { + return Long.valueOf(num0.longValue() - num1.longValue()); + } + + protected Number multiply(Number num0, Number num1) { + return Long.valueOf(num0.longValue() * num1.longValue()); + } + + public boolean matches(Object obj0, Object obj1) { + return (obj0 instanceof Long || obj1 instanceof Long); + } + } + + public final static BigDecimalDelegate BIGDECIMAL = new BigDecimalDelegate(); + + public final static BigIntegerDelegate BIGINTEGER = new BigIntegerDelegate(); + + public final static DoubleDelegate DOUBLE = new DoubleDelegate(); + + public final static LongDelegate LONG = new LongDelegate(); + + private final static Long ZERO = Long.valueOf(0); + + public final static Number add(final Object obj0, final Object obj1) { + if (obj0 == null && obj1 == null) { + return Long.valueOf(0); + } + + final ELArithmetic delegate; + if (BIGDECIMAL.matches(obj0, obj1)) + delegate = BIGDECIMAL; + else if (DOUBLE.matches(obj0, obj1)) + delegate = DOUBLE; + else if (BIGINTEGER.matches(obj0, obj1)) + delegate = BIGINTEGER; + else + delegate = LONG; + + Number num0 = delegate.coerce(obj0); + Number num1 = delegate.coerce(obj1); + + return delegate.add(num0, num1); + } + + public final static Number mod(final Object obj0, final Object obj1) { + if (obj0 == null && obj1 == null) { + return Long.valueOf(0); + } + + final ELArithmetic delegate; + if (BIGDECIMAL.matches(obj0, obj1)) + delegate = BIGDECIMAL; + else if (DOUBLE.matches(obj0, obj1)) + delegate = DOUBLE; + else if (BIGINTEGER.matches(obj0, obj1)) + delegate = BIGINTEGER; + else + delegate = LONG; + + Number num0 = delegate.coerce(obj0); + Number num1 = delegate.coerce(obj1); + + return delegate.mod(num0, num1); + } + + public final static Number subtract(final Object obj0, final Object obj1) { + if (obj0 == null && obj1 == null) { + return Long.valueOf(0); + } + + final ELArithmetic delegate; + if (BIGDECIMAL.matches(obj0, obj1)) + delegate = BIGDECIMAL; + else if (DOUBLE.matches(obj0, obj1)) + delegate = DOUBLE; + else if (BIGINTEGER.matches(obj0, obj1)) + delegate = BIGINTEGER; + else + delegate = LONG; + + Number num0 = delegate.coerce(obj0); + Number num1 = delegate.coerce(obj1); + + return delegate.subtract(num0, num1); + } + + public final static Number divide(final Object obj0, final Object obj1) { + if (obj0 == null && obj1 == null) { + return ZERO; + } + + final ELArithmetic delegate; + if (BIGDECIMAL.matches(obj0, obj1)) + delegate = BIGDECIMAL; + else if (BIGINTEGER.matches(obj0, obj1)) + delegate = BIGDECIMAL; + else + delegate = DOUBLE; + + Number num0 = delegate.coerce(obj0); + Number num1 = delegate.coerce(obj1); + + return delegate.divide(num0, num1); + } + + public final static Number multiply(final Object obj0, final Object obj1) { + if (obj0 == null && obj1 == null) { + return Long.valueOf(0); + } + + final ELArithmetic delegate; + if (BIGDECIMAL.matches(obj0, obj1)) + delegate = BIGDECIMAL; + else if (DOUBLE.matches(obj0, obj1)) + delegate = DOUBLE; + else if (BIGINTEGER.matches(obj0, obj1)) + delegate = BIGINTEGER; + else + delegate = LONG; + + Number num0 = delegate.coerce(obj0); + Number num1 = delegate.coerce(obj1); + + return delegate.multiply(num0, num1); + } + + public final static boolean isNumber(final Object obj) { + return (obj != null && isNumberType(obj.getClass())); + } + + public final static boolean isNumberType(final Class type) { + return type == (java.lang.Long.class) || type == Long.TYPE || type == (java.lang.Double.class) || type == Double.TYPE || type == (java.lang.Byte.class) || type == Byte.TYPE || type == (java.lang.Short.class) || type == Short.TYPE || type == (java.lang.Integer.class) || type == Integer.TYPE || type == (java.lang.Float.class) || type == Float.TYPE || type == (java.math.BigInteger.class) || type == (java.math.BigDecimal.class); + } + + /** + * + */ + protected ELArithmetic() { + super(); + } + + protected abstract Number add(final Number num0, final Number num1); + + protected abstract Number multiply(final Number num0, final Number num1); + + protected abstract Number subtract(final Number num0, final Number num1); + + protected abstract Number mod(final Number num0, final Number num1); + + protected abstract Number coerce(final Number num); + + protected final Number coerce(final Object obj) { + + if (isNumber(obj)) { + return coerce((Number) obj); + } + if (obj instanceof String) { + return coerce((String) obj); + } + if (obj == null || "".equals(obj)) { + return coerce(ZERO); + } + + Class objType = obj.getClass(); + if (Character.class.equals(objType) || Character.TYPE == objType) { + return coerce(Short.valueOf((short) ((Character) obj).charValue())); + } + + throw new IllegalArgumentException(MessageFactory.get("el.convert", obj, + objType)); + } + + protected abstract Number coerce(final String str); + + protected abstract Number divide(final Number num0, final Number num1); + + protected abstract boolean matches(final Object obj0, final Object obj1); + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/lang/ELSupport.java b/fine-third-default/fine-javax-el/src/com/sun/el/lang/ELSupport.java new file mode 100644 index 000000000..dc216988e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/lang/ELSupport.java @@ -0,0 +1,518 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.lang; + +import java.beans.PropertyEditor; +import java.beans.PropertyEditorManager; +import java.math.BigDecimal; +import java.math.BigInteger; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.PropertyNotFoundException; + +import com.sun.el.util.MessageFactory; + +/** + * A helper class that implements the EL Specification + * + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class ELSupport { + + private final static Long ZERO = Long.valueOf(0L); + + public final static void throwUnhandled(Object base, Object property) + throws ELException { + if (base == null) { + throw new PropertyNotFoundException(MessageFactory.get( + "error.resolver.unhandled.null", property)); + } else { + throw new PropertyNotFoundException(MessageFactory.get( + "error.resolver.unhandled", base.getClass(), property)); + } + } + + /** + * @param obj0 First object to be compared + * @param obj1 Second object to be compared + * @return The result (an int with values -1, 0, or 1) of the comparison + * @throws EvaluationException + */ + public final static int compare(final Object obj0, final Object obj1) + throws ELException { + if (obj0 == obj1 || equals(obj0, obj1)) { + return 0; + } + if (isBigDecimalOp(obj0, obj1)) { + BigDecimal bd0 = (BigDecimal) coerceToNumber(obj0, BigDecimal.class); + BigDecimal bd1 = (BigDecimal) coerceToNumber(obj1, BigDecimal.class); + return bd0.compareTo(bd1); + } + if (isDoubleOp(obj0, obj1)) { + Double d0 = (Double) coerceToNumber(obj0, Double.class); + Double d1 = (Double) coerceToNumber(obj1, Double.class); + return d0.compareTo(d1); + } + if (isBigIntegerOp(obj0, obj1)) { + BigInteger bi0 = (BigInteger) coerceToNumber(obj0, BigInteger.class); + BigInteger bi1 = (BigInteger) coerceToNumber(obj1, BigInteger.class); + return bi0.compareTo(bi1); + } + if (isLongOp(obj0, obj1)) { + Long l0 = (Long) coerceToNumber(obj0, Long.class); + Long l1 = (Long) coerceToNumber(obj1, Long.class); + return l0.compareTo(l1); + } + if (obj0 instanceof String || obj1 instanceof String) { + return coerceToString(obj0).compareTo(coerceToString(obj1)); + } + if (obj0 instanceof Comparable) { + // Safe cast + @SuppressWarnings("unchecked") + Comparable cobj0 = (Comparable) obj0; + return (obj1 != null) ? cobj0.compareTo(obj1) : 1; + } + if (obj1 instanceof Comparable) { + // Safe cast + @SuppressWarnings("unchecked") + Comparable cobj1 = (Comparable) obj1; + return (obj0 != null) ? -(cobj1.compareTo(obj0)) : -1; + } + throw new ELException(MessageFactory.get("error.compare", obj0, obj1)); + } + + /** + * @param obj0 Fisrt object to be compared + * @param obj1 Second object to be compared + * @return true if the objects compared equal + * @throws EvaluationException + */ + public final static boolean equals(final Object obj0, final Object obj1) + throws ELException { + if (obj0 == obj1) { + return true; + } + if (obj0 == null || obj1 == null) { + return false; + } + if (obj0 instanceof Boolean || obj1 instanceof Boolean) { + return coerceToBoolean(obj0).equals(coerceToBoolean(obj1)); + } + if (obj0.getClass().isEnum()) { + return obj0.equals(coerceToEnum(obj1, obj0.getClass())); + } + if (obj1.getClass().isEnum()) { + return obj1.equals(coerceToEnum(obj0, obj1.getClass())); + } + if (obj0 instanceof String || obj1 instanceof String) { + return coerceToString(obj0).equals(coerceToString(obj1)); + } + if (isBigDecimalOp(obj0, obj1)) { + BigDecimal bd0 = (BigDecimal) coerceToNumber(obj0, BigDecimal.class); + BigDecimal bd1 = (BigDecimal) coerceToNumber(obj1, BigDecimal.class); + return bd0.equals(bd1); + } + if (isDoubleOp(obj0, obj1)) { + Double d0 = (Double) coerceToNumber(obj0, Double.class); + Double d1 = (Double) coerceToNumber(obj1, Double.class); + return d0.equals(d1); + } + if (isBigIntegerOp(obj0, obj1)) { + BigInteger bi0 = (BigInteger) coerceToNumber(obj0, BigInteger.class); + BigInteger bi1 = (BigInteger) coerceToNumber(obj1, BigInteger.class); + return bi0.equals(bi1); + } + if (isLongOp(obj0, obj1)) { + Long l0 = (Long) coerceToNumber(obj0, Long.class); + Long l1 = (Long) coerceToNumber(obj1, Long.class); + return l0.equals(l1); + } else { + return obj0.equals(obj1); + } + } + + /** + * @param obj Object to be coerced + * @return The result of coercion + */ + public final static Boolean coerceToBoolean(final Object obj) + throws IllegalArgumentException { + if (obj == null || "".equals(obj)) { + return Boolean.FALSE; + } + if (obj instanceof Boolean) { + return (Boolean) obj; + } + if (obj instanceof String) { + return Boolean.valueOf((String) obj); + } + + throw new IllegalArgumentException(MessageFactory.get("error.convert", + obj, obj.getClass(), Boolean.class)); + } + + // Enum types are hard construct. We can declare this as + // > T coerceToEnum(Object, Class type) + // but this makes it harder to get the calls right. + @SuppressWarnings("unchecked") + public final static Enum coerceToEnum(final Object obj, Class type) { + if (obj == null || "".equals(obj)) { + return null; + } + if (obj.getClass().isEnum()) { + return (Enum)obj; + } + return Enum.valueOf(type, obj.toString()); + } + + public final static Character coerceToCharacter(final Object obj) + throws IllegalArgumentException { + if (obj == null || "".equals(obj)) { + return Character.valueOf((char) 0); + } + if (obj instanceof String) { + return Character.valueOf(((String) obj).charAt(0)); + } + if (ELArithmetic.isNumber(obj)) { + return Character.valueOf((char) ((Number) obj).shortValue()); + } + Class objType = obj.getClass(); + if (obj instanceof Character) { + return (Character) obj; + } + + throw new IllegalArgumentException(MessageFactory.get("error.convert", + obj, objType, Character.class)); + } + + public final static Number coerceToNumber(final Object obj) { + if (obj == null) { + return ZERO; + } else if (obj instanceof Number) { + return (Number) obj; + } else { + String str = coerceToString(obj); + if (isStringFloat(str)) { + return toFloat(str); + } else { + return toNumber(str); + } + } + } + + protected final static Number coerceToNumber(final Number number, + final Class type) throws IllegalArgumentException { + if (Long.TYPE == type || Long.class.equals(type)) { + return Long.valueOf(number.longValue()); + } + if (Double.TYPE == type || Double.class.equals(type)) { + return Double.valueOf(number.doubleValue()); + } + if (Integer.TYPE == type || Integer.class.equals(type)) { + return Integer.valueOf(number.intValue()); + } + if (BigInteger.class.equals(type)) { + if (number instanceof BigDecimal) { + return ((BigDecimal) number).toBigInteger(); + } + if (number instanceof BigInteger) { + return number; + } + return BigInteger.valueOf(number.longValue()); + } + if (BigDecimal.class.equals(type)) { + if (number instanceof BigDecimal) { + return number; + } + if (number instanceof BigInteger) { + return new BigDecimal((BigInteger) number); + } + if (number instanceof Long) { + return new BigDecimal((Long) number); + } + return new BigDecimal(number.doubleValue()); + } + if (Byte.TYPE == type || Byte.class.equals(type)) { + return Byte.valueOf(number.byteValue()); + } + if (Short.TYPE == type || Short.class.equals(type)) { + return Short.valueOf(number.shortValue()); + } + if (Float.TYPE == type || Float.class.equals(type)) { + return Float.valueOf(number.floatValue()); + } + + throw new IllegalArgumentException(MessageFactory.get("error.convert", + number, number.getClass(), type)); + } + + public final static Number coerceToNumber(final Object obj, final Class type) + throws IllegalArgumentException { + if (obj == null || "".equals(obj)) { + return coerceToNumber(ZERO, type); + } + if (obj instanceof String) { + return coerceToNumber((String) obj, type); + } + if (ELArithmetic.isNumber(obj)) { + if (obj.getClass().equals(type)) { + return (Number) obj; + } + return coerceToNumber((Number) obj, type); + } + + if (obj instanceof Character) { + return coerceToNumber(Short.valueOf((short) ((Character) obj) + .charValue()), type); + } + + throw new IllegalArgumentException(MessageFactory.get("error.convert", + obj, obj.getClass(), type)); + } + + protected final static Number coerceToNumber(final String val, + final Class type) throws IllegalArgumentException { + if (Long.TYPE == type || Long.class.equals(type)) { + return Long.valueOf(val); + } + if (Integer.TYPE == type || Integer.class.equals(type)) { + return Integer.valueOf(val); + } + if (Double.TYPE == type || Double.class.equals(type)) { + return Double.valueOf(val); + } + if (BigInteger.class.equals(type)) { + return new BigInteger(val); + } + if (BigDecimal.class.equals(type)) { + return new BigDecimal(val); + } + if (Byte.TYPE == type || Byte.class.equals(type)) { + return Byte.valueOf(val); + } + if (Short.TYPE == type || Short.class.equals(type)) { + return Short.valueOf(val); + } + if (Float.TYPE == type || Float.class.equals(type)) { + return Float.valueOf(val); + } + + throw new IllegalArgumentException(MessageFactory.get("error.convert", + val, String.class, type)); + } + + /** + * @param obj Object to be coerced + * @return The result of coercion + */ + public final static String coerceToString(final Object obj) { + if (obj == null) { + return ""; + } else if (obj instanceof String) { + return (String) obj; + } else if (obj instanceof Enum) { + return ((Enum) obj).name(); + } else { + return obj.toString(); + } + } + + public final static void checkType(final Object obj, final Class type) + throws IllegalArgumentException { + if (String.class.equals(type)) { + coerceToString(obj); + } + if (ELArithmetic.isNumberType(type)) { + coerceToNumber(obj, type); + } + if (Character.class.equals(type) || Character.TYPE == type) { + coerceToCharacter(obj); + } + if (Boolean.class.equals(type) || Boolean.TYPE == type) { + coerceToBoolean(obj); + } + if (type.isEnum()) { + coerceToEnum(obj, type); + } + } + + public final static Object coerceToType(final Object obj, final Class type) + throws IllegalArgumentException { + if (type == null || Object.class.equals(type) || + (obj != null && type.isAssignableFrom(obj.getClass()))) { + return obj; + } + + // new to EL 3.0 + if (obj == null && !type.isPrimitive() && !String.class.equals(type)) { + return null; + } + + if (String.class.equals(type)) { + return coerceToString(obj); + } + if (ELArithmetic.isNumberType(type)) { + return coerceToNumber(obj, type); + } + if (Character.class.equals(type) || Character.TYPE == type) { + return coerceToCharacter(obj); + } + if (Boolean.class.equals(type) || Boolean.TYPE == type) { + return coerceToBoolean(obj); + } + if (type.isEnum()) { + return coerceToEnum(obj, type); + } + + if (obj instanceof String) { + if ("".equals(obj)) + return null; + PropertyEditor editor = PropertyEditorManager.findEditor(type); + if (editor != null) { + editor.setAsText((String) obj); + return editor.getValue(); + } + } + throw new IllegalArgumentException(MessageFactory.get("error.convert", + obj, obj.getClass(), type)); + } + + /** + * @param obj An array of objects + * @return true if the array contains a null, false otherwise + */ + public final static boolean containsNulls(final Object[] obj) { + for (int i = 0; i < obj.length; i++) { + if (obj[0] == null) { + return true; + } + } + return false; + } + + public final static boolean isBigDecimalOp(final Object obj0, + final Object obj1) { + return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal); + } + + public final static boolean isBigIntegerOp(final Object obj0, + final Object obj1) { + return (obj0 instanceof BigInteger || obj1 instanceof BigInteger); + } + + public final static boolean isDoubleOp(final Object obj0, final Object obj1) { + return (obj0 instanceof Double + || obj1 instanceof Double + || obj0 instanceof Float + || obj1 instanceof Float); + } + + public final static boolean isDoubleStringOp(final Object obj0, + final Object obj1) { + return (isDoubleOp(obj0, obj1) + || (obj0 instanceof String && isStringFloat((String) obj0)) || (obj1 instanceof String && isStringFloat((String) obj1))); + } + + public final static boolean isLongOp(final Object obj0, final Object obj1) { + return (obj0 instanceof Long + || obj1 instanceof Long + || obj0 instanceof Integer + || obj1 instanceof Integer + || obj0 instanceof Character + || obj1 instanceof Character + || obj0 instanceof Short + || obj1 instanceof Short + || obj0 instanceof Byte + || obj1 instanceof Byte); + } + + public final static boolean isStringFloat(final String str) { + int len = str.length(); + if (len > 1) { + for (int i = 0; i < len; i++) { + switch (str.charAt(i)) { + case 'E': + return true; + case 'e': + return true; + case '.': + return true; + } + } + } + return false; + } + + public final static Number toFloat(final String value) { + try { + if (Double.parseDouble(value) > Double.MAX_VALUE) { + return new BigDecimal(value); + } else { + return Double.valueOf(value); + } + } catch (NumberFormatException e0) { + return new BigDecimal(value); + } + } + + public final static Number toNumber(final String value) { + try { + return Integer.valueOf(Integer.parseInt(value)); + } catch (NumberFormatException e0) { + try { + return Long.valueOf(Long.parseLong(value)); + } catch (NumberFormatException e1) { + return new BigInteger(value); + } + } + } + + /** + * + */ + public ELSupport() { + super(); + } + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/lang/EvaluationContext.java b/fine-third-default/fine-javax-el/src/com/sun/el/lang/EvaluationContext.java new file mode 100644 index 000000000..fb33bd842 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/lang/EvaluationContext.java @@ -0,0 +1,173 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.lang; + +import java.util.EventListener; +import java.util.List; +import java.util.Map; + +import javax.el.ELContext; +import javax.el.ELResolver; +import javax.el.FunctionMapper; +import javax.el.VariableMapper; +import javax.el.TypeConverter; +import javax.el.ImportHandler; +import javax.el.EvaluationListener; + +/** + * The context for EL expression evaluation. This wrapper ELContext captures + * the function mapper and the variable mapper at the point when the epxression + * is parsed, and only for those functions and variable used in the expression. + */ +public final class EvaluationContext extends ELContext { + + private final ELContext elContext; + + private final FunctionMapper fnMapper; + + private final VariableMapper varMapper; + + public EvaluationContext(ELContext elContext, FunctionMapper fnMapper, + VariableMapper varMapper) { + this.elContext = elContext; + this.fnMapper = fnMapper; + this.varMapper = varMapper; + } + + public ELContext getELContext() { + return this.elContext; + } + + @Override + public FunctionMapper getFunctionMapper() { + return this.fnMapper; + } + + @Override + public VariableMapper getVariableMapper() { + return this.varMapper; + } + + @Override + public Object getContext(Class key) { + return this.elContext.getContext(key); + } + + @Override + public ELResolver getELResolver() { + return this.elContext.getELResolver(); + } + + @Override + public boolean isPropertyResolved() { + return this.elContext.isPropertyResolved(); + } + + @Override + public void putContext(Class key, Object contextObject) { + this.elContext.putContext(key, contextObject); + } + + @Override + public void setPropertyResolved(boolean resolved) { + this.elContext.setPropertyResolved(resolved); + } + + @Override + public void setPropertyResolved(Object base, Object property) { + this.elContext.setPropertyResolved(base, property); + } + + @Override + public void addEvaluationListener(EvaluationListener listener) { + this.elContext.addEvaluationListener(listener); + } + + @Override + public List getEvaluationListeners() { + return this.elContext.getEvaluationListeners(); + } + + @Override + public void notifyBeforeEvaluation(String expr) { + this.elContext.notifyBeforeEvaluation(expr); + } + + @Override + public void notifyAfterEvaluation(String expr) { + this.elContext.notifyAfterEvaluation(expr); + } + + @Override + public void notifyPropertyResolved(Object base, Object property) { + this.elContext.notifyPropertyResolved(base, property); + } + + @Override + public boolean isLambdaArgument(String arg) { + return this.elContext.isLambdaArgument(arg); + } + + @Override + public Object getLambdaArgument(String arg) { + return this.elContext.getLambdaArgument(arg); + } + + @Override + public void enterLambdaScope(Map args) { + this.elContext.enterLambdaScope(args); + } + + @Override + public void exitLambdaScope() { + this.elContext.exitLambdaScope(); + } + + @Override + public Object convertToType(Object obj, Class targetType) { + return this.elContext.convertToType(obj, targetType); + } + + @Override + public ImportHandler getImportHandler() { + return this.elContext.getImportHandler(); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/lang/ExpressionBuilder.java b/fine-third-default/fine-javax-el/src/com/sun/el/lang/ExpressionBuilder.java new file mode 100644 index 000000000..7e5b8668e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/lang/ExpressionBuilder.java @@ -0,0 +1,315 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.lang; + +import java.io.StringReader; +import java.lang.reflect.Method; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.lang.ref.ReferenceQueue; +import java.util.Map; +import java.util.Collections; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.FunctionMapper; +import javax.el.MethodExpression; +import javax.el.ValueExpression; +import javax.el.VariableMapper; + +import com.sun.el.MethodExpressionImpl; +import com.sun.el.MethodExpressionLiteral; +import com.sun.el.ValueExpressionImpl; +import com.sun.el.parser.AstCompositeExpression; +import com.sun.el.parser.AstDeferredExpression; +import com.sun.el.parser.AstDynamicExpression; +import com.sun.el.parser.AstFunction; +import com.sun.el.parser.AstIdentifier; +import com.sun.el.parser.AstLiteralExpression; +import com.sun.el.parser.AstMethodArguments; +import com.sun.el.parser.AstValue; +import com.sun.el.parser.ELParser; +import com.sun.el.parser.Node; +import com.sun.el.parser.NodeVisitor; +import com.sun.el.parser.ParseException; +import com.sun.el.util.MessageFactory; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung // EL cache + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class ExpressionBuilder implements NodeVisitor { + + static private class NodeSoftReference extends SoftReference { + final String key; + NodeSoftReference(String key, Node node, ReferenceQueue refQ) { + super(node, refQ); + this.key = key; + } + } + + static private class SoftConcurrentHashMap extends + ConcurrentHashMap { + + private static final int CACHE_INIT_SIZE = 256; + private ConcurrentHashMap map = + new ConcurrentHashMap(CACHE_INIT_SIZE); + private ReferenceQueue refQ = new ReferenceQueue(); + + // Remove map entries that have been placed on the queue by GC. + private void cleanup() { + NodeSoftReference nodeRef = null; + while ((nodeRef = (NodeSoftReference)refQ.poll()) != null) { + map.remove(nodeRef.key); + } + } + + @Override + public Node put(String key, Node value) { + cleanup(); + NodeSoftReference prev = + map.put(key, new NodeSoftReference(key, value, refQ)); + return prev == null? null: prev.get(); + } + + @Override + public Node putIfAbsent(String key, Node value) { + cleanup(); + NodeSoftReference prev = + map.putIfAbsent(key, new NodeSoftReference(key, value, refQ)); + return prev == null? null: prev.get(); + } + + @Override + public Node get(Object key) { + cleanup(); + NodeSoftReference nodeRef = map.get(key); + if (nodeRef == null) { + return null; + } + if (nodeRef.get() == null) { + // value has been garbage collected, remove entry in map + map.remove(key); + return null; + } + return nodeRef.get(); + } + } + + private static final SoftConcurrentHashMap cache = + new SoftConcurrentHashMap(); + private FunctionMapper fnMapper; + private VariableMapper varMapper; + private String expression; + + /** + * + */ + public ExpressionBuilder(String expression, ELContext ctx) + throws ELException { + this.expression = expression; + + FunctionMapper ctxFn = ctx.getFunctionMapper(); + VariableMapper ctxVar = ctx.getVariableMapper(); + + if (ctxFn != null) { + this.fnMapper = new FunctionMapperFactory(ctxFn); + } + if (ctxVar != null) { + this.varMapper = new VariableMapperFactory(ctxVar); + } + } + + public final static Node createNode(String expr) throws ELException { + Node n = createNodeInternal(expr); + return n; + } + + private final static Node createNodeInternal(String expr) + throws ELException { + if (expr == null) { + throw new ELException(MessageFactory.get("error.null")); + } + + Node n = cache.get(expr); + if (n == null) { + try { + n = (new ELParser( + new com.sun.el.parser.ELParserTokenManager( + new com.sun.el.parser.SimpleCharStream( + new StringReader(expr),1, 1, expr.length()+1)))) + .CompositeExpression(); + + // validate composite expression + if (n instanceof AstCompositeExpression) { + int numChildren = n.jjtGetNumChildren(); + if (numChildren == 1) { + n = n.jjtGetChild(0); + } else { + Class type = null; + Node child = null; + for (int i = 0; i < numChildren; i++) { + child = n.jjtGetChild(i); + if (child instanceof AstLiteralExpression) + continue; + if (type == null) + type = child.getClass(); + else { + if (!type.equals(child.getClass())) { + throw new ELException(MessageFactory.get( + "error.mixed", expr)); + } + } + } + } + } + if (n instanceof AstDeferredExpression + || n instanceof AstDynamicExpression) { + n = n.jjtGetChild(0); + } + cache.putIfAbsent(expr, n); + } catch (ParseException pe) { + throw new ELException("Error Parsing: " + expr, pe); + } + } + return n; + } + + /** + * Scan the expression nodes and captures the functions and variables used + * in this expression. This ensures that any changes to the functions or + * variables mappings during the expression will not affect the evaluation + * of this expression, as the functions and variables are bound and + * resolved at parse time, as specified in the spec. + */ + private void prepare(Node node) throws ELException { + node.accept(this); + if (this.fnMapper instanceof FunctionMapperFactory) { + this.fnMapper = ((FunctionMapperFactory) this.fnMapper).create(); + } + if (this.varMapper instanceof VariableMapperFactory) { + this.varMapper = ((VariableMapperFactory) this.varMapper).create(); + } + } + + private Node build() throws ELException { + Node n = createNodeInternal(this.expression); + this.prepare(n); + if (n instanceof AstDeferredExpression + || n instanceof AstDynamicExpression) { + n = n.jjtGetChild(0); + } + return n; + } + + /* + * (non-Javadoc) + * + * @see com.sun.el.parser.NodeVisitor#visit(com.sun.el.parser.Node) + */ + public void visit(Node node) throws ELException { + if (node instanceof AstFunction) { + AstFunction funcNode = (AstFunction) node; + if ((funcNode.getPrefix().length() == 0) && + (this.fnMapper == null || fnMapper.resolveFunction( + funcNode.getPrefix(), + funcNode.getLocalName()) == null)) { + // This can be a call to a LambdaExpression. The target + // of the call is a bean or an EL variable. Capture + // the variable name in the variable mapper if it is an + // variable. The decision to invoke the static method or + // the LambdaExpression will be made at runtime. + if (this.varMapper != null) { + this.varMapper.resolveVariable(funcNode.getLocalName()); + } + return; + } + + if (this.fnMapper == null) { + throw new ELException(MessageFactory.get("error.fnMapper.null")); + } + Method m = fnMapper.resolveFunction(funcNode.getPrefix(), funcNode + .getLocalName()); + if (m == null) { + throw new ELException(MessageFactory.get( + "error.fnMapper.method", funcNode.getOutputName())); + } + int pcnt = m.getParameterTypes().length; + int acnt = ((AstMethodArguments)node.jjtGetChild(0)).getParameterCount(); + if (acnt != pcnt) { + throw new ELException(MessageFactory.get( + "error.fnMapper.paramcount", funcNode.getOutputName(), + "" + pcnt, "" + acnt)); + } + } else if (node instanceof AstIdentifier && this.varMapper != null) { + String variable = ((AstIdentifier) node).getImage(); + + // simply capture it + this.varMapper.resolveVariable(variable); + } + } + + public ValueExpression createValueExpression(Class expectedType) + throws ELException { + Node n = this.build(); + return new ValueExpressionImpl(this.expression, n, this.fnMapper, + this.varMapper, expectedType); + } + + public MethodExpression createMethodExpression(Class expectedReturnType, + Class[] expectedParamTypes) throws ELException { + Node n = this.build(); + if (n instanceof AstValue || n instanceof AstIdentifier) { + return new MethodExpressionImpl(expression, n, + this.fnMapper, this.varMapper, + expectedReturnType, expectedParamTypes); + } else if (n instanceof AstLiteralExpression) { + return new MethodExpressionLiteral(expression, expectedReturnType, + expectedParamTypes); + } else { + throw new ELException("Not a Valid Method Expression: " + + expression); + } + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/lang/ExpressionBuilder.java.sav b/fine-third-default/fine-javax-el/src/com/sun/el/lang/ExpressionBuilder.java.sav new file mode 100644 index 000000000..7db476956 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/lang/ExpressionBuilder.java.sav @@ -0,0 +1,265 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.lang; + +import java.io.StringReader; +import java.lang.reflect.Method; +import java.lang.ref.SoftReference; +import java.util.Map; +import java.util.Collections; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.FunctionMapper; +import javax.el.MethodExpression; +import javax.el.ValueExpression; +import javax.el.VariableMapper; + +import com.sun.el.MethodExpressionImpl; +import com.sun.el.MethodExpressionLiteral; +import com.sun.el.ValueExpressionImpl; +import com.sun.el.parser.AstCompositeExpression; +import com.sun.el.parser.AstDeferredExpression; +import com.sun.el.parser.AstDynamicExpression; +import com.sun.el.parser.AstFunction; +import com.sun.el.parser.AstIdentifier; +import com.sun.el.parser.AstLiteralExpression; +import com.sun.el.parser.AstValue; +import com.sun.el.parser.ELParser; +import com.sun.el.parser.Node; +import com.sun.el.parser.NodeVisitor; +import com.sun.el.parser.ParseException; +import com.sun.el.util.MessageFactory; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class ExpressionBuilder implements NodeVisitor { + + private static final int CACHE_INIT_SIZE = 256; + private static final ConcurrentHashMap cache = + new ConcurrentHashMap(CACHE_INIT_SIZE) { + + private ConcurrentHashMap> map = + new ConcurrentHashMap>(CACHE_INIT_SIZE); + + @Override + public Node put(String key, Node value) { + SoftReference ref = new SoftReference(value); + SoftReference prev = map.put(key, ref); + return prev == null? null: prev.get(); + } + + @Override + public Node putIfAbsent(String key, Node value) { + SoftReference ref = new SoftReference(value); + SoftReference prev = map.putIfAbsent(key, ref); + return prev == null? null: prev.get(); + } + + @Override + public Node get(Object key) { + SoftReference ref = map.get(key); + if (ref != null && ref.get() == null) { + map.remove(key); + } + return ref != null ? ref.get() : null; + } + }; + + private FunctionMapper fnMapper; + + private VariableMapper varMapper; + + private String expression; + + /** + * + */ + public ExpressionBuilder(String expression, ELContext ctx) + throws ELException { + this.expression = expression; + + FunctionMapper ctxFn = ctx.getFunctionMapper(); + VariableMapper ctxVar = ctx.getVariableMapper(); + + if (ctxFn != null) { + this.fnMapper = new FunctionMapperFactory(ctxFn); + } + if (ctxVar != null) { + this.varMapper = new VariableMapperFactory(ctxVar); + } + } + + public final static Node createNode(String expr) throws ELException { + Node n = createNodeInternal(expr); + return n; + } + + private final static Node createNodeInternal(String expr) + throws ELException { + if (expr == null) { + throw new ELException(MessageFactory.get("error.null")); + } + + Node n = (Node) cache.get(expr); + if (n == null) { + try { + n = (new ELParser( + new com.sun.el.parser.ELParserTokenManager( + new com.sun.el.parser.SimpleCharStream( + new StringReader(expr),1, 1, expr.length()+1)))) + .CompositeExpression(); + + // validate composite expression + if (n instanceof AstCompositeExpression) { + int numChildren = n.jjtGetNumChildren(); + if (numChildren == 1) { + n = n.jjtGetChild(0); + } else { + Class type = null; + Node child = null; + for (int i = 0; i < numChildren; i++) { + child = n.jjtGetChild(i); + if (child instanceof AstLiteralExpression) + continue; + if (type == null) + type = child.getClass(); + else { + if (!type.equals(child.getClass())) { + throw new ELException(MessageFactory.get( + "error.mixed", expr)); + } + } + } + } + } + if (n instanceof AstDeferredExpression + || n instanceof AstDynamicExpression) { + n = n.jjtGetChild(0); + } + cache.putIfAbsent(expr, n); + } catch (ParseException pe) { + throw new ELException("Error Parsing: " + expr, pe); + } + } + return n; + } + + private void prepare(Node node) throws ELException { + node.accept(this); + if (this.fnMapper instanceof FunctionMapperFactory) { + this.fnMapper = ((FunctionMapperFactory) this.fnMapper).create(); + } + if (this.varMapper instanceof VariableMapperFactory) { + this.varMapper = ((VariableMapperFactory) this.varMapper).create(); + } + } + + private Node build() throws ELException { + Node n = createNodeInternal(this.expression); + this.prepare(n); + if (n instanceof AstDeferredExpression + || n instanceof AstDynamicExpression) { + n = n.jjtGetChild(0); + } + return n; + } + + /* + * (non-Javadoc) + * + * @see com.sun.el.parser.NodeVisitor#visit(com.sun.el.parser.Node) + */ + public void visit(Node node) throws ELException { + if (node instanceof AstFunction) { + + AstFunction funcNode = (AstFunction) node; + + if (this.fnMapper == null) { + throw new ELException(MessageFactory.get("error.fnMapper.null")); + } + Method m = fnMapper.resolveFunction(funcNode.getPrefix(), funcNode + .getLocalName()); + if (m == null) { + throw new ELException(MessageFactory.get( + "error.fnMapper.method", funcNode.getOutputName())); + } + int pcnt = m.getParameterTypes().length; + if (node.jjtGetNumChildren() != pcnt) { + throw new ELException(MessageFactory.get( + "error.fnMapper.paramcount", funcNode.getOutputName(), + "" + pcnt, "" + node.jjtGetNumChildren())); + } + } else if (node instanceof AstIdentifier && this.varMapper != null) { + String variable = ((AstIdentifier) node).getImage(); + + // simply capture it + this.varMapper.resolveVariable(variable); + } + } + + public ValueExpression createValueExpression(Class expectedType) + throws ELException { + Node n = this.build(); + return new ValueExpressionImpl(this.expression, n, this.fnMapper, + this.varMapper, expectedType); + } + + public MethodExpression createMethodExpression(Class expectedReturnType, + Class[] expectedParamTypes) throws ELException { + Node n = this.build(); + if (n instanceof AstValue || n instanceof AstIdentifier) { + return new MethodExpressionImpl(expression, n, + this.fnMapper, this.varMapper, expectedReturnType, + expectedParamTypes); + } else if (n instanceof AstLiteralExpression) { + return new MethodExpressionLiteral(expression, expectedReturnType, + expectedParamTypes); + } else { + throw new ELException("Not a Valid Method Expression: " + + expression); + } + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/lang/FunctionMapperFactory.java b/fine-third-default/fine-javax-el/src/com/sun/el/lang/FunctionMapperFactory.java new file mode 100644 index 000000000..eba9ea6f3 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/lang/FunctionMapperFactory.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.lang; + +import java.lang.reflect.Method; + +import javax.el.FunctionMapper; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class FunctionMapperFactory extends FunctionMapper { + + protected FunctionMapperImpl memento = null; + protected FunctionMapper target; + + public FunctionMapperFactory(FunctionMapper mapper) { + if (mapper == null) { + throw new NullPointerException("FunctionMapper target cannot be null"); + } + this.target = mapper; + } + + + /* (non-Javadoc) + * @see javax.el.FunctionMapper#resolveFunction(java.lang.String, java.lang.String) + */ + public Method resolveFunction(String prefix, String localName) { + if (this.memento == null) { + this.memento = new FunctionMapperImpl(); + } + Method m = this.target.resolveFunction(prefix, localName); + if (m != null) { + this.memento.addFunction(prefix, localName, m); + } + return m; + } + + public FunctionMapper create() { + return this.memento; + } + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/lang/FunctionMapperImpl.java b/fine-third-default/fine-javax-el/src/com/sun/el/lang/FunctionMapperImpl.java new file mode 100644 index 000000000..afab9c017 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/lang/FunctionMapperImpl.java @@ -0,0 +1,222 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.lang; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import javax.el.FunctionMapper; + +import com.sun.el.util.ReflectionUtil; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class FunctionMapperImpl extends FunctionMapper implements + Externalizable { + + private static final long serialVersionUID = 1L; + + protected Map functions = null; + + /* + * (non-Javadoc) + * + * @see javax.el.FunctionMapper#resolveFunction(java.lang.String, + * java.lang.String) + */ + public Method resolveFunction(String prefix, String localName) { + if (this.functions != null) { + Function f = this.functions.get(prefix + ":" + localName); + return f.getMethod(); + } + return null; + } + + public void addFunction(String prefix, String localName, Method m) { + if (this.functions == null) { + this.functions = new HashMap(); + } + Function f = new Function(prefix, localName, m); + synchronized (this) { + this.functions.put(prefix+":"+localName, f); + } + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) + */ + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(this.functions); + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#readExternal(java.io.ObjectInput) + */ + // Safe cast + @SuppressWarnings("unchecked") + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + this.functions = (Map) in.readObject(); + } + + public static class Function implements Externalizable { + + protected transient Method m; + protected String owner; + protected String name; + protected String[] types; + protected String prefix; + protected String localName; + + /** + * + */ + public Function(String prefix, String localName, Method m) { + if (localName == null) { + throw new NullPointerException("LocalName cannot be null"); + } + if (m == null) { + throw new NullPointerException("Method cannot be null"); + } + this.prefix = prefix; + this.localName = localName; + this.m = m; + } + + public Function() { + // for serialization + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) + */ + public void writeExternal(ObjectOutput out) throws IOException { + + out.writeUTF((this.prefix != null) ? this.prefix : ""); + out.writeUTF(this.localName); + + if (this.owner != null) { + out.writeUTF(this.owner); + } else { + out.writeUTF(this.m.getDeclaringClass().getName()); + } + if (this.name != null) { + out.writeUTF(this.name); + } else { + out.writeUTF(this.m.getName()); + } + if (this.types != null) { + out.writeObject(this.types); + } else { + out.writeObject(ReflectionUtil.toTypeNameArray(this.m.getParameterTypes())); + } + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#readExternal(java.io.ObjectInput) + */ + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + + this.prefix = in.readUTF(); + if ("".equals(this.prefix)) this.prefix = null; + this.localName = in.readUTF(); + this.owner = in.readUTF(); + this.name = in.readUTF(); + this.types = (String[]) in.readObject(); + } + + public Method getMethod() { + if (this.m == null) { + try { + Class t = Class.forName(this.owner, false, + Thread.currentThread().getContextClassLoader()); + Class[] p = ReflectionUtil.toTypeArray(this.types); + this.m = t.getMethod(this.name, p); + } catch (Exception e) { + e.printStackTrace(); + } + } + return this.m; + } + + public boolean matches(String prefix, String localName) { + if (this.prefix != null) { + if (prefix == null) return false; + if (!this.prefix.equals(prefix)) return false; + } + return this.localName.equals(localName); + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj instanceof Function) { + return this.hashCode() == obj.hashCode(); + } + return false; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return (this.prefix + this.localName).hashCode(); + } + } + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/lang/VariableMapperFactory.java b/fine-third-default/fine-javax-el/src/com/sun/el/lang/VariableMapperFactory.java new file mode 100644 index 000000000..65e929188 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/lang/VariableMapperFactory.java @@ -0,0 +1,79 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.lang; + +import javax.el.ValueExpression; +import javax.el.VariableMapper; + +/** + * Creates a VariableMapper for the variables used in the expression. + */ +public class VariableMapperFactory extends VariableMapper { + + private final VariableMapper target; + private VariableMapper momento; + + public VariableMapperFactory(VariableMapper target) { + if (target == null) { + throw new NullPointerException("Target VariableMapper cannot be null"); + } + this.target = target; + } + + public VariableMapper create() { + return this.momento; + } + + public ValueExpression resolveVariable(String variable) { + ValueExpression expr = this.target.resolveVariable(variable); + if (expr != null) { + if (this.momento == null) { + this.momento = new VariableMapperImpl(); + } + this.momento.setVariable(variable, expr); + } + return expr; + } + + public ValueExpression setVariable(String variable, ValueExpression expression) { + throw new UnsupportedOperationException("Cannot Set Variables on Factory"); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/lang/VariableMapperImpl.java b/fine-third-default/fine-javax-el/src/com/sun/el/lang/VariableMapperImpl.java new file mode 100644 index 000000000..79d326429 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/lang/VariableMapperImpl.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.lang; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.HashMap; +import java.util.Map; + +import javax.el.ValueExpression; +import javax.el.VariableMapper; + +public class VariableMapperImpl extends VariableMapper implements Externalizable { + + private static final long serialVersionUID = 1L; + + private Map vars = + new HashMap(); + + public VariableMapperImpl() { + super(); + } + + public ValueExpression resolveVariable(String variable) { + return this.vars.get(variable); + } + + public ValueExpression setVariable(String variable, + ValueExpression expression) { + return this.vars.put(variable, expression); + } + + // Safe cast. + @SuppressWarnings("unchecked") + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + this.vars = (Map) in.readObject(); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(this.vars); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/ArithmeticNode.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ArithmeticNode.java new file mode 100644 index 000000000..a6920272d --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ArithmeticNode.java @@ -0,0 +1,64 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class ArithmeticNode extends SimpleNode { + + /** + * @param i + */ + public ArithmeticNode(int i) { + super(i); + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return Number.class; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstAnd.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstAnd.java new file mode 100644 index 000000000..681b9991f --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstAnd.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstAnd extends BooleanNode { + public AstAnd(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj = children[0].getValue(ctx); + Boolean b = coerceToBoolean(obj); + if (!b.booleanValue()) { + return b; + } + obj = children[1].getValue(ctx); + b = coerceToBoolean(obj); + return b; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstAssign.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstAssign.java new file mode 100644 index 000000000..51f1494bd --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstAssign.java @@ -0,0 +1,62 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; +import javax.el.VariableMapper; +import com.sun.el.ValueExpressionImpl; +import com.sun.el.lang.EvaluationContext; + +public +class AstAssign extends SimpleNode { + public AstAssign(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + + Object value = children[1].getValue(ctx); + children[0].setValue(ctx, value); + return value; + } +} + diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstBracketSuffix.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstBracketSuffix.java new file mode 100644 index 000000000..7562960e0 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstBracketSuffix.java @@ -0,0 +1,61 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstBracketSuffix extends SimpleNode { + public AstBracketSuffix(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return this.children[0].getValue(ctx); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstChoice.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstChoice.java new file mode 100644 index 000000000..d524df56e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstChoice.java @@ -0,0 +1,93 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstChoice extends SimpleNode { + public AstChoice(int id) { + super(id); + } + + public Class getType(EvaluationContext ctx) + throws ELException { + Object val = this.getValue(ctx); + return (val != null) ? val.getClass() : null; + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Boolean b0 = coerceToBoolean(obj0); + return this.children[((b0.booleanValue() ? 1 : 2))].getValue(ctx); + } + + public boolean isReadOnly(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Boolean b0 = coerceToBoolean(obj0); + return this.children[((b0.booleanValue() ? 1 : 2))].isReadOnly(ctx); + } + + public void setValue(EvaluationContext ctx, Object value) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Boolean b0 = coerceToBoolean(obj0); + this.children[((b0.booleanValue()? 1: 2))].setValue(ctx, value); + } + + public Object invoke(EvaluationContext ctx, + Class[] paramTypes, + Object[] paramValues) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Boolean b0 = coerceToBoolean(obj0); + return this.children[((b0.booleanValue() ? 1 : 2))] + .invoke(ctx, paramTypes, paramValues); + } + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstCompositeExpression.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstCompositeExpression.java new file mode 100644 index 000000000..4deba9e5e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstCompositeExpression.java @@ -0,0 +1,76 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstCompositeExpression extends SimpleNode { + + public AstCompositeExpression(int id) { + super(id); + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return String.class; + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + StringBuffer sb = new StringBuffer(16); + Object obj = null; + if (this.children != null) { + for (int i = 0; i < this.children.length; i++) { + obj = this.children[i].getValue(ctx); + if (obj != null) { + sb.append(obj); + } + } + } + return sb.toString(); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstConcat.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstConcat.java new file mode 100644 index 000000000..89b6e37bd --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstConcat.java @@ -0,0 +1,61 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Kin-man Chung + */ +public final +class AstConcat extends SimpleNode { + public AstConcat(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return children[0].getValue(ctx).toString() + + children[1].getValue(ctx).toString(); + } +} + diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDeferredExpression.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDeferredExpression.java new file mode 100644 index 000000000..a7d858cee --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDeferredExpression.java @@ -0,0 +1,75 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstDeferredExpression extends SimpleNode { + public AstDeferredExpression(int id) { + super(id); + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return this.children[0].getType(ctx); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return this.children[0].getValue(ctx); + } + + public boolean isReadOnly(EvaluationContext ctx) + throws ELException { + return this.children[0].isReadOnly(ctx); + } + + public void setValue(EvaluationContext ctx, Object value) + throws ELException { + this.children[0].setValue(ctx, value); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDiv.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDiv.java new file mode 100644 index 000000000..6b6ee4ae8 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDiv.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstDiv extends ArithmeticNode { + public AstDiv(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return ELArithmetic.divide(obj0, obj1); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDotSuffix.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDotSuffix.java new file mode 100644 index 000000000..cac4b4578 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDotSuffix.java @@ -0,0 +1,60 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstDotSuffix extends SimpleNode { + public AstDotSuffix(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return this.image; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDynamicExpression.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDynamicExpression.java new file mode 100644 index 000000000..8ab701d4b --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstDynamicExpression.java @@ -0,0 +1,75 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstDynamicExpression extends SimpleNode { + public AstDynamicExpression(int id) { + super(id); + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return this.children[0].getType(ctx); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return this.children[0].getValue(ctx); + } + + public boolean isReadOnly(EvaluationContext ctx) + throws ELException { + return this.children[0].isReadOnly(ctx); + } + + public void setValue(EvaluationContext ctx, Object value) + throws ELException { + this.children[0].setValue(ctx, value); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstEmpty.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstEmpty.java new file mode 100644 index 000000000..dc0d37f5e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstEmpty.java @@ -0,0 +1,80 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.util.Collection; +import java.util.Map; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstEmpty extends SimpleNode { + public AstEmpty(int id) { + super(id); + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return Boolean.class; + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj = this.children[0].getValue(ctx); + if (obj == null) { + return Boolean.TRUE; + } else if (obj instanceof String) { + return Boolean.valueOf(((String) obj).length() == 0); + } else if (obj instanceof Object[]) { + return Boolean.valueOf(((Object[]) obj).length == 0); + } else if (obj instanceof Collection) { + return Boolean.valueOf(((Collection) obj).isEmpty()); + } else if (obj instanceof Map) { + return Boolean.valueOf(((Map) obj).isEmpty()); + } + return Boolean.FALSE; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstEqual.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstEqual.java new file mode 100644 index 000000000..8662b14f6 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstEqual.java @@ -0,0 +1,62 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstEqual extends BooleanNode { + public AstEqual(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return Boolean.valueOf(equals(obj0, obj1)); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstFalse.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstFalse.java new file mode 100644 index 000000000..6de2c9b74 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstFalse.java @@ -0,0 +1,60 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstFalse extends BooleanNode { + public AstFalse(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return Boolean.FALSE; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstFloatingPoint.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstFloatingPoint.java new file mode 100644 index 000000000..c5493b240 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstFloatingPoint.java @@ -0,0 +1,80 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.math.BigDecimal; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstFloatingPoint extends SimpleNode { + public AstFloatingPoint(int id) { + super(id); + } + + private Number number; + + public Number getFloatingPoint() { + if (this.number == null) { + try { + this.number = Double.valueOf(this.image); + } catch (ArithmeticException e0) { + this.number = new BigDecimal(this.image); + } + } + return this.number; + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return this.getFloatingPoint(); + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return this.getFloatingPoint().getClass(); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstFunction.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstFunction.java new file mode 100644 index 000000000..9dc5dae8d --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstFunction.java @@ -0,0 +1,227 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.el.ELException; +import javax.el.FunctionMapper; +import javax.el.LambdaExpression; +import javax.el.ValueExpression; +import javax.el.VariableMapper; +import javax.el.ELClass; + +import com.sun.el.lang.EvaluationContext; +import com.sun.el.util.MessageFactory; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstFunction extends SimpleNode { + + protected String localName = ""; + + protected String prefix = ""; + + public AstFunction(int id) { + super(id); + } + + public String getLocalName() { + return localName; + } + + public String getOutputName() { + if (this.prefix.length() == 0) { + return this.localName; + } else { + return this.prefix + ":" + this.localName; + } + } + + public String getPrefix() { + return prefix; + } + + @Override + public Class getType(EvaluationContext ctx) + throws ELException { + + FunctionMapper fnMapper = ctx.getFunctionMapper(); + + // quickly validate again for this request + if (fnMapper == null) { + throw new ELException(MessageFactory.get("error.fnMapper.null")); + } + Method m = fnMapper.resolveFunction(this.prefix, this.localName); + if (m == null) { + throw new ELException(MessageFactory.get("error.fnMapper.method", + this.getOutputName())); + } + return m.getReturnType(); + } + + /* + * Find the object associated with the given name. Return null if the + * there is no such object. + */ + private Object findValue(EvaluationContext ctx, String name) { + Object value; + // First check if this is a Lambda argument + if (ctx.isLambdaArgument(name)) { + return ctx.getLambdaArgument(name); + } + + // Next check if this an EL variable + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(name); + if (expr != null) { + return expr.getValue(ctx.getELContext()); + } + } + // Check if this is resolvable by an ELResolver + ctx.setPropertyResolved(false); + Object ret = ctx.getELResolver().getValue(ctx, null, name); + if (ctx.isPropertyResolved()) { + return ret; + } + return null; + } + + @Override + public Object getValue(EvaluationContext ctx) + throws ELException { + + // Check to see if a function is a bean that is a Lambdaexpression. + // If so, invoke it. Also allow for the case that a Lambda expression + // can return another Lambda expression. + if (prefix.length() == 0) { + Object val = findValue(ctx, this.localName); + // Check the case of repeated lambda invocation, such as f()()() + + if ((val != null) && (val instanceof LambdaExpression)) { + for (int i = 0; i < this.children.length; i++) { + Object[] params = ((AstMethodArguments)this.children[i]). + getParameters(ctx); + if (! (val instanceof LambdaExpression)) { + throw new ELException(MessageFactory.get( + "error.function.syntax", getOutputName())); + } + val = ((LambdaExpression)val).invoke(ctx, params); + } + return val; + } + } + + FunctionMapper fnMapper = ctx.getFunctionMapper(); + + // quickly validate again for this request + if (fnMapper == null) { + throw new ELException(MessageFactory.get("error.fnMapper.null")); + } + Method m = fnMapper.resolveFunction(this.prefix, this.localName); + if (m == null) { + if (this.prefix.length() == 0 && ctx.getImportHandler() != null) { + Class c = null;; + // Check if this is a constructor call for an imported class + c = ctx.getImportHandler().resolveClass(this.localName); + String methodName = null; + if (c != null) { + methodName = ""; + } else { + // Check if this is a imported static method + c = ctx.getImportHandler().resolveStatic(this.localName); + methodName = this.localName;; + } + if (c != null) { + // Use StaticFieldELResolver to invoke the constructor or the + // static method. + Object[] params = + ((AstMethodArguments)this.children[0]).getParameters(ctx); + return ctx.getELResolver().invoke(ctx, new ELClass(c), + methodName, null, params); + } + } + throw new ELException(MessageFactory.get("error.fnMapper.method", + this.getOutputName())); + } + + Class[] paramTypes = m.getParameterTypes(); + Object[] params = + ((AstMethodArguments)this.children[0]).getParameters(ctx); + Object result = null; + for (int i = 0; i < params.length; i++) { + try { + params[i] = ctx.convertToType(params[i], paramTypes[i]); + } catch (ELException ele) { + throw new ELException(MessageFactory.get("error.function", this + .getOutputName()), ele); + } + } + try { + result = m.invoke(null, params); + } catch (IllegalAccessException iae) { + throw new ELException(MessageFactory.get("error.function", this + .getOutputName()), iae); + } catch (InvocationTargetException ite) { + throw new ELException(MessageFactory.get("error.function", this + .getOutputName()), ite.getCause()); + } + return result; + } + + public void setLocalName(String localName) { + this.localName = localName; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + @Override + public String toString() + { + return ELParserTreeConstants.jjtNodeName[id] + "[" + this.getOutputName() + "]"; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstGreaterThan.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstGreaterThan.java new file mode 100644 index 000000000..ebd8cd2b5 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstGreaterThan.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstGreaterThan extends BooleanNode { + public AstGreaterThan(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + if (obj0 == null) { + return Boolean.FALSE; + } + Object obj1 = this.children[1].getValue(ctx); + if (obj1 == null) { + return Boolean.FALSE; + } + return (compare(obj0, obj1) > 0) ? Boolean.TRUE : Boolean.FALSE; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstGreaterThanEqual.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstGreaterThanEqual.java new file mode 100644 index 000000000..7f3415fa8 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstGreaterThanEqual.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstGreaterThanEqual extends BooleanNode { + public AstGreaterThanEqual(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + if (obj0 == obj1) { + return Boolean.TRUE; + } + if (obj0 == null || obj1 == null) { + return Boolean.FALSE; + } + return (compare(obj0, obj1) >= 0) ? Boolean.TRUE : Boolean.FALSE; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstIdentifier.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstIdentifier.java new file mode 100644 index 000000000..6709e2202 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstIdentifier.java @@ -0,0 +1,239 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; +import javax.el.ELResolver; +import javax.el.MethodExpression; +import javax.el.MethodInfo; +import javax.el.MethodNotFoundException; +import javax.el.ValueExpression; +import javax.el.VariableMapper; +import javax.el.ValueReference; +import javax.el.PropertyNotWritableException; +import javax.el.ELClass; + +import com.sun.el.lang.EvaluationContext; +import com.sun.el.lang.ELSupport; +import com.sun.el.util.MessageFactory; + + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstIdentifier extends SimpleNode { + public AstIdentifier(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + // First check if this is a lambda argument + if (ctx.isLambdaArgument(this.image)) { + return Object.class; + } + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(this.image); + if (expr != null) { + return expr.getType(ctx.getELContext()); + } + } + ctx.setPropertyResolved(false); + Class ret = ctx.getELResolver().getType(ctx, null, this.image); + if (! ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(null, this.image); + } + return ret; + } + + public ValueReference getValueReference(EvaluationContext ctx) + throws ELException { + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(this.image); + if (expr != null) { + return expr.getValueReference(ctx.getELContext()); + } + } + return new ValueReference(null, this.image); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + // First check if this is a lambda argument + if (ctx.isLambdaArgument(this.image)) { + return ctx.getLambdaArgument(this.image); + } + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(this.image); + if (expr != null) { + return expr.getValue(ctx.getELContext()); + } + } + ctx.setPropertyResolved(false); + Object ret = ctx.getELResolver().getValue(ctx, null, this.image); + if (! ctx.isPropertyResolved()) { + // Check if this is an imported static field + if (ctx.getImportHandler() != null) { + Class c = ctx.getImportHandler().resolveStatic(this.image); + if (c != null) { + return ctx.getELResolver().getValue(ctx, new ELClass(c), + this.image); + } + } + ELSupport.throwUnhandled(null, this.image); + } + return ret; + } + + public boolean isReadOnly(EvaluationContext ctx) throws ELException { + // Lambda arguments are read only. + if (ctx.isLambdaArgument(this.image)) { + return true; + } + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(this.image); + if (expr != null) { + return expr.isReadOnly(ctx.getELContext()); + } + } + ctx.setPropertyResolved(false); + boolean ret = ctx.getELResolver().isReadOnly(ctx, null, this.image); + if (! ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(null, this.image); + } + return ret; + } + + public void setValue(EvaluationContext ctx, Object value) + throws ELException { + // First check if this is a lambda argument + if (ctx.isLambdaArgument(this.image)) { + throw new PropertyNotWritableException( + MessageFactory.get("error.lambda.parameter.readonly", + this.image)); + } + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(this.image); + if (expr != null) { + expr.setValue(ctx.getELContext(), value); + return; + } + } + ctx.setPropertyResolved(false); + ELResolver elResolver = ctx.getELResolver(); + elResolver.setValue(ctx, null, this.image, value); + if (! ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(null, this.image); + } + } + + private final Object invokeTarget(EvaluationContext ctx, Object target, + Object[] paramValues) throws ELException { + if (target instanceof MethodExpression) { + MethodExpression me = (MethodExpression) target; + return me.invoke(ctx.getELContext(), paramValues); + } else if (target == null) { + throw new MethodNotFoundException("Identity '" + this.image + + "' was null and was unable to invoke"); + } else { + throw new ELException( + "Identity '" + + this.image + + "' does not reference a MethodExpression instance, returned type: " + + target.getClass().getName()); + } + } + + public Object invoke(EvaluationContext ctx, Class[] paramTypes, + Object[] paramValues) throws ELException { + return this.getMethodExpression(ctx).invoke(ctx.getELContext(), paramValues); + } + + + public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) + throws ELException { + return this.getMethodExpression(ctx).getMethodInfo(ctx.getELContext()); + } + + private final MethodExpression getMethodExpression(EvaluationContext ctx) + throws ELException { + Object obj = null; + + // case A: ValueExpression exists, getValue which must + // be a MethodExpression + VariableMapper varMapper = ctx.getVariableMapper(); + ValueExpression ve = null; + if (varMapper != null) { + ve = varMapper.resolveVariable(this.image); + if (ve != null) { + obj = ve.getValue(ctx); + } + } + + // case B: evaluate the identity against the ELResolver, again, must be + // a MethodExpression to be able to invoke + if (ve == null) { + ctx.setPropertyResolved(false); + obj = ctx.getELResolver().getValue(ctx, null, this.image); + } + + // finally provide helpful hints + if (obj instanceof MethodExpression) { + return (MethodExpression) obj; + } else if (obj == null) { + throw new MethodNotFoundException("Identity '" + this.image + + "' was null and was unable to invoke"); + } else { + throw new ELException( + "Identity '" + + this.image + + "' does not reference a MethodExpression instance, returned type: " + + obj.getClass().getName()); + } + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstInteger.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstInteger.java new file mode 100644 index 000000000..51f643764 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstInteger.java @@ -0,0 +1,80 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.math.BigInteger; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstInteger extends SimpleNode { + public AstInteger(int id) { + super(id); + } + + private Number number; + + protected Number getInteger() { + if (this.number == null) { + try { + this.number = Long.valueOf(this.image); + } catch (ArithmeticException e1) { + this.number = new BigInteger(this.image); + } + } + return number; + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return this.getInteger().getClass(); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return this.getInteger(); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLambdaExpression.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLambdaExpression.java new file mode 100644 index 000000000..7586a69c1 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLambdaExpression.java @@ -0,0 +1,91 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.util.List; +import javax.el.ELException; +import javax.el.ValueExpression; +import javax.el.LambdaExpression; +import com.sun.el.lang.EvaluationContext; +import com.sun.el.ValueExpressionImpl; +import com.sun.el.util.MessageFactory; + +/** + * @author Kin-man Chung + */ +public +class AstLambdaExpression extends SimpleNode { + + public AstLambdaExpression(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) throws ELException { + // Create a lambda expression + ValueExpression expr = + new ValueExpressionImpl("#{Lambda Expression}", + this.children[1], + ctx.getFunctionMapper(), + ctx.getVariableMapper(), + null); + Listparameters = + ((AstLambdaParameters) this.children[0]).getParameters(); + LambdaExpression lambda = new LambdaExpression(parameters, expr); + if (this.children.length <= 2) { + return lambda; + } + + // There are arguments following the lambda exprn, invoke it now. + Object ret = null; + for (int i = 2; i < this.children.length; i++) { + if (ret != null) { + if (!(ret instanceof LambdaExpression)) { + throw new ELException(MessageFactory.get( + "error.lambda.call")); + } + lambda = (LambdaExpression) ret; + } + AstMethodArguments args = (AstMethodArguments) this.children[i]; + ret = lambda.invoke(ctx, args.getParameters(ctx)); + } + return ret; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLambdaParameters.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLambdaParameters.java new file mode 100644 index 000000000..b01fbadd4 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLambdaParameters.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.util.List; +import java.util.ArrayList; + +/** + * @author Kin-man Chung + */ + +public +class AstLambdaParameters extends SimpleNode { + public AstLambdaParameters(int id) { + super(id); + } + + List getParameters() { + List parameters = new ArrayList(); + if (children != null) { + for (Node child: children) { + parameters.add(child.getImage()); + } + } + return parameters; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLessThan.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLessThan.java new file mode 100644 index 000000000..d0f8b4502 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLessThan.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstLessThan extends BooleanNode { + public AstLessThan(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + if (obj0 == null) { + return Boolean.FALSE; + } + Object obj1 = this.children[1].getValue(ctx); + if (obj1 == null) { + return Boolean.FALSE; + } + return (compare(obj0, obj1) < 0) ? Boolean.TRUE : Boolean.FALSE; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLessThanEqual.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLessThanEqual.java new file mode 100644 index 000000000..740365b23 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLessThanEqual.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstLessThanEqual extends BooleanNode { + public AstLessThanEqual(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + if (obj0 == obj1) { + return Boolean.TRUE; + } + if (obj0 == null || obj1 == null) { + return Boolean.FALSE; + } + return (compare(obj0, obj1) <= 0) ? Boolean.TRUE : Boolean.FALSE; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstListData.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstListData.java new file mode 100644 index 000000000..f991c44d0 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstListData.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.util.ArrayList; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Kin-man Chung + */ +public +class AstListData extends SimpleNode { + public AstListData(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) { + ArrayList list = new ArrayList(); + int paramCount = this.jjtGetNumChildren(); + for (int i = 0; i < paramCount; i++) { + list.add(this.children[i].getValue(ctx)); + } + return list; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLiteralExpression.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLiteralExpression.java new file mode 100644 index 000000000..d72921267 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstLiteralExpression.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstLiteralExpression extends SimpleNode { + public AstLiteralExpression(int id) { + super(id); + } + + public Class getType(EvaluationContext ctx) throws ELException { + return String.class; + } + + public Object getValue(EvaluationContext ctx) throws ELException { + return this.image; + } + + public void setImage(String image) { + if (image.indexOf('\\') == -1) { + this.image = image; + return; + } + int size = image.length(); + StringBuffer buf = new StringBuffer(size); + for (int i = 0; i < size; i++) { + char c = image.charAt(i); + if (c == '\\' && i + 1 < size) { + char c1 = image.charAt(i + 1); + if (c1 == '\\' || c1 == '"' || c1 == '\'' || c1 == '#' + || c1 == '$') { + c = c1; + i++; + } + } + buf.append(c); + } + this.image = buf.toString(); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMapData.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMapData.java new file mode 100644 index 000000000..4ed25e606 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMapData.java @@ -0,0 +1,81 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.util.HashSet; +import java.util.HashMap; +import javax.el.ELException; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Kin-man Chung + */ +public +class AstMapData extends SimpleNode { + public AstMapData(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) { + HashSet set = new HashSet(); + HashMap map = new HashMap(); + + int paramCount = this.jjtGetNumChildren(); + for (int i = 0; i < paramCount; i++) { + Node entry = this.children[i]; + Object v1 = entry.jjtGetChild(0).getValue(ctx); + if (entry.jjtGetNumChildren() > 1) { + // expr: expr + map.put(v1, entry.jjtGetChild(1).getValue(ctx)); + } else { + set.add(v1); + } + } + // It is error to have mixed set/map entries + if (set.size() > 0 && map.size() > 0) { + throw new ELException("Cannot mix set entry with map entry."); + } + if (map.size() > 0) { + return map; + } + return set; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMapEntry.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMapEntry.java new file mode 100644 index 000000000..76bb224dd --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMapEntry.java @@ -0,0 +1,51 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +/** + * @author Kin-man Chung + */ +public +class AstMapEntry extends SimpleNode { + public AstMapEntry(int id) { + super(id); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMethodArguments.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMethodArguments.java new file mode 100644 index 000000000..676903de9 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMethodArguments.java @@ -0,0 +1,80 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Kin-man Chung + */ +public +class AstMethodArguments extends SimpleNode { + public AstMethodArguments(int id) { + super(id); + } + + Class[] getParamTypes () { + return null; + } + + public Object[] getParameters(EvaluationContext ctx) throws ELException { + + if (this.children == null) + return new Object[] {}; + + Object[] obj = new Object[this.children.length]; + for (int i = 0; i < obj.length; i++) { + obj[i] = this.children[i].getValue(ctx); + } + return obj; + } + + public int getParameterCount() { + return this.children == null? 0: this.children.length; + } + + @Override + public boolean isParametersProvided() { + return true; + } + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMinus.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMinus.java new file mode 100644 index 000000000..9337cbacc --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMinus.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstMinus extends ArithmeticNode { + public AstMinus(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return ELArithmetic.subtract(obj0, obj1); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMod.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMod.java new file mode 100644 index 000000000..3cd3bda5b --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMod.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstMod extends ArithmeticNode { + public AstMod(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return ELArithmetic.mod(obj0, obj1); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMult.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMult.java new file mode 100644 index 000000000..6767baa01 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstMult.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstMult extends ArithmeticNode { + public AstMult(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return ELArithmetic.multiply(obj0, obj1); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNegative.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNegative.java new file mode 100644 index 000000000..4fe414507 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNegative.java @@ -0,0 +1,105 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstNegative extends SimpleNode { + public AstNegative(int id) { + super(id); + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return Number.class; + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj = this.children[0].getValue(ctx); + + if (obj == null) { + return Long.valueOf(0); + } + if (obj instanceof BigDecimal) { + return ((BigDecimal) obj).negate(); + } + if (obj instanceof BigInteger) { + return ((BigInteger) obj).negate(); + } + if (obj instanceof String) { + if (isStringFloat((String) obj)) { + return Double.valueOf(-Double.parseDouble((String) obj)); + } + return Long.valueOf(-Long.parseLong((String) obj)); + } + Class type = obj.getClass(); + if (obj instanceof Long || Long.TYPE == type) { + return Long.valueOf(-((Long) obj).longValue()); + } + if (obj instanceof Double || Double.TYPE == type) { + return Double.valueOf(-((Double) obj).doubleValue()); + } + if (obj instanceof Integer || Integer.TYPE == type) { + return Integer.valueOf(-((Integer) obj).intValue()); + } + if (obj instanceof Float || Float.TYPE == type) { + return Float.valueOf(-((Float) obj).floatValue()); + } + if (obj instanceof Short || Short.TYPE == type) { + return Short.valueOf((short) -((Short) obj).shortValue()); + } + if (obj instanceof Byte || Byte.TYPE == type) { + return Byte.valueOf((byte) -((Byte) obj).byteValue()); + } + Long num = (Long) coerceToNumber(obj, Long.class); + return Long.valueOf(-num.longValue()); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNot.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNot.java new file mode 100644 index 000000000..ae1c601db --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNot.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstNot extends SimpleNode { + public AstNot(int id) { + super(id); + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return Boolean.class; + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj = this.children[0].getValue(ctx); + Boolean b = coerceToBoolean(obj); + return Boolean.valueOf(!b.booleanValue()); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNotEqual.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNotEqual.java new file mode 100644 index 000000000..9d884434e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNotEqual.java @@ -0,0 +1,62 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstNotEqual extends BooleanNode { + public AstNotEqual(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return Boolean.valueOf(!equals(obj0, obj1)); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNull.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNull.java new file mode 100644 index 000000000..9776a709a --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstNull.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstNull extends SimpleNode { + public AstNull(int id) { + super(id); + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return null; + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return null; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstOr.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstOr.java new file mode 100644 index 000000000..e3cefde63 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstOr.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstOr extends BooleanNode { + public AstOr(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj = this.children[0].getValue(ctx); + Boolean b = coerceToBoolean(obj); + if (b.booleanValue()) { + return b; + } + obj = this.children[1].getValue(ctx); + b = coerceToBoolean(obj); + return b; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstPlus.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstPlus.java new file mode 100644 index 000000000..6d0060532 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstPlus.java @@ -0,0 +1,64 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstPlus extends ArithmeticNode { + public AstPlus(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return ELArithmetic.add(obj0, obj1); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstPropertySuffix.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstPropertySuffix.java new file mode 100644 index 000000000..cf85f315b --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstPropertySuffix.java @@ -0,0 +1,56 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +public final class AstPropertySuffix extends SimpleNode { + public AstPropertySuffix(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return this.image; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstSemiColon.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstSemiColon.java new file mode 100644 index 000000000..aee5b62a3 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstSemiColon.java @@ -0,0 +1,66 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Kin-man Chung + */ +public +class AstSemiColon extends SimpleNode { + public AstSemiColon(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + this.children[0].getValue(ctx); + return this.children[1].getValue(ctx); + } + + public void setValue(EvaluationContext ctx, Object value) + throws ELException { + this.children[0].getValue(ctx); + this.children[1].setValue(ctx, value); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstString.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstString.java new file mode 100644 index 000000000..a8c928ddf --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstString.java @@ -0,0 +1,96 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstString extends SimpleNode { + public AstString(int id) { + super(id); + } + + private String string; + + public String getString() { + if (this.string == null) { + this.string = this.image.substring(1, this.image.length() - 1); + } + return this.string; + } + + public Class getType(EvaluationContext ctx) + throws ELException { + return String.class; + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return this.getString(); + } + + public void setImage(String image) { + if (image.indexOf('\\') == -1) { + this.image = image; + return; + } + int size = image.length(); + StringBuffer buf = new StringBuffer(size); + for (int i = 0; i < size; i++) { + char c = image.charAt(i); + if (c == '\\' && i + 1 < size) { + char c1 = image.charAt(i + 1); + if (c1 == '\\' || c1 == '"' || c1 == '\'' || c1 == '#' + || c1 == '$') { + c = c1; + i++; + } + } + buf.append(c); + } + this.image = buf.toString(); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstTrue.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstTrue.java new file mode 100644 index 000000000..e9042f711 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstTrue.java @@ -0,0 +1,60 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstTrue extends BooleanNode { + public AstTrue(int id) { + super(id); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + return Boolean.TRUE; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstValue.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstValue.java new file mode 100644 index 000000000..3f178bd41 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/AstValue.java @@ -0,0 +1,288 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.el.ELException; +import javax.el.ELResolver; +import javax.el.MethodInfo; +import javax.el.ValueReference; +import javax.el.ELClass; +import javax.el.PropertyNotFoundException; +import javax.el.PropertyNotWritableException; +import javax.el.ImportHandler; + +import com.sun.el.lang.EvaluationContext; +import com.sun.el.lang.ELSupport; +import com.sun.el.util.MessageFactory; +import com.sun.el.util.ReflectionUtil; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstValue extends SimpleNode { + + protected static class Target { + protected Object base; + protected Node suffixNode; + + Target(Object base, Node suffixNode) { + this.base = base; + this.suffixNode = suffixNode; + } + + boolean isMethodCall() { + return getArguments(suffixNode) != null; + } + } + + public AstValue(int id) { + super(id); + } + + public Class getType(EvaluationContext ctx) throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + return null; + } + Object property = t.suffixNode.getValue(ctx); + ctx.setPropertyResolved(false); + Class ret = ctx.getELResolver().getType(ctx, t.base, property); + if (! ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(t.base, property); + } + return ret; + } + + public ValueReference getValueReference(EvaluationContext ctx) + throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + return null; + } + Object property = t.suffixNode.getValue(ctx); + return new ValueReference(t.base, property); + } + + private static AstMethodArguments getArguments(Node n) { + if (n instanceof AstDotSuffix && n.jjtGetNumChildren() > 0) { + return (AstMethodArguments) n.jjtGetChild(0); + } + if (n instanceof AstBracketSuffix && n.jjtGetNumChildren() > 1) { + return (AstMethodArguments) n.jjtGetChild(1); + } + return null; + } + + private Object getValue(Object base, Node child, EvaluationContext ctx) + throws ELException { + + Object value = null; + ELResolver resolver = ctx.getELResolver(); + Object property = child.getValue(ctx); + AstMethodArguments args = getArguments(child); + if (args != null) { + // This is a method call + if (! (property instanceof String)) { + throw new ELException(MessageFactory.get( + "error.method.name", property)); + } + Class[] paramTypes = args.getParamTypes(); + Object[] params = args.getParameters(ctx); + + ctx.setPropertyResolved(false); + value = resolver.invoke(ctx, base, property, paramTypes, params); + } else { + if (property != null) { + ctx.setPropertyResolved(false); + value = resolver.getValue(ctx, base, property); + if (! ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(base, property); + } + } + } + return value; + } + + private final Object getBase(EvaluationContext ctx) { + try { + return this.children[0].getValue(ctx); + } catch (PropertyNotFoundException ex) { + // Next check if the base is an imported class + if (this.children[0] instanceof AstIdentifier) { + String name = ((AstIdentifier) this.children[0]).image; + ImportHandler importHandler = ctx.getImportHandler(); + if (importHandler != null) { + Class c = importHandler.resolveClass(name); + if (c != null) { + return new ELClass(c); + } + } + } + throw ex; + } + } + + private final Target getTarget(EvaluationContext ctx) throws ELException { + // evaluate expr-a to value-a + Object base = getBase(ctx); + + // if our base is null (we know there are more properites to evaluate) + if (base == null) { + throw new PropertyNotFoundException(MessageFactory.get( + "error.unreachable.base", this.children[0].getImage())); + } + + // set up our start/end + Object property = null; + int propCount = this.jjtGetNumChildren() - 1; + int i = 1; + + // evaluate any properties before our target + if (propCount > 1) { + while (base != null && i < propCount) { + base = getValue(base, this.children[i], ctx); + i++; + } + // if we are in this block, we have more properties to resolve, + // but our base was null + if (base == null) { + throw new PropertyNotFoundException(MessageFactory.get( + "error.unreachable.property", property)); + } + } + return new Target(base, this.children[propCount]); + } + + public Object getValue(EvaluationContext ctx) throws ELException { + Object base = getBase(ctx); + int propCount = this.jjtGetNumChildren(); + int i = 1; + while (base != null && i < propCount) { + base = getValue(base, this.children[i], ctx); + i++; + } + return base; + } + + public boolean isReadOnly(EvaluationContext ctx) throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + return true; + } + Object property = t.suffixNode.getValue(ctx); + ctx.setPropertyResolved(false); + boolean ret = ctx.getELResolver().isReadOnly(ctx, t.base, property); + if (! ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(t.base, property); + } + return ret; + } + + public void setValue(EvaluationContext ctx, Object value) + throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + throw new PropertyNotWritableException( + MessageFactory.get("error.syntax.set")); + } + Object property = t.suffixNode.getValue(ctx); + ctx.setPropertyResolved(false); + ELResolver elResolver = ctx.getELResolver(); + + value = ctx.convertToType(value, + elResolver.getType(ctx, t.base, property)); + + elResolver.setValue(ctx, t.base, property, value); + if (! ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(t.base, property); + } + } + + public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) + throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + return null; + } + Object property = t.suffixNode.getValue(ctx); + Method m = ReflectionUtil.getMethod(t.base, property, paramTypes); + return new MethodInfo(m.getName(), m.getReturnType(), m + .getParameterTypes()); + } + + public Object invoke(EvaluationContext ctx, Class[] paramTypes, + Object[] paramValues) throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + AstMethodArguments args = getArguments(t.suffixNode); + // Always use the param types in the expression, and ignore those + // specified elsewhere, such as TLD + paramTypes = args.getParamTypes(); + Object[] params = args.getParameters(ctx); + String method = (String) t.suffixNode.getValue(ctx); + + ctx.setPropertyResolved(false); + ELResolver resolver = ctx.getELResolver(); + return resolver.invoke(ctx, t.base, method, paramTypes, params); + } + Object property = t.suffixNode.getValue(ctx); + Method m = ReflectionUtil.getMethod(t.base, property, paramTypes); + Object result = null; + try { + result = m.invoke(t.base, (Object[]) paramValues); + } catch (IllegalAccessException iae) { + throw new ELException(iae); + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } + return result; + } + + @Override + public boolean isParametersProvided() { + return getArguments(this.children[this.jjtGetNumChildren()-1]) != null; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/BooleanNode.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/BooleanNode.java new file mode 100644 index 000000000..538996e88 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/BooleanNode.java @@ -0,0 +1,62 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class BooleanNode extends SimpleNode { + /** + * @param i + */ + public BooleanNode(int i) { + super(i); + } + public Class getType(EvaluationContext ctx) + throws ELException { + return Boolean.class; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParser.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParser.java new file mode 100644 index 000000000..ea2212f32 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParser.java @@ -0,0 +1,3013 @@ +/* Generated By:JJTree&JavaCC: Do not edit this line. ELParser.java */ +package com.sun.el.parser; +import java.io.StringReader; +import javax.el.ELException; +public class ELParser/*@bgen(jjtree)*/implements ELParserTreeConstants, ELParserConstants {/*@bgen(jjtree)*/ + protected JJTELParserState jjtree = new JJTELParserState();public static Node parse(String ref) throws ELException + { + try { + return (new ELParser(new StringReader(ref))).CompositeExpression(); + } catch (ParseException pe) { + throw new ELException(pe.getMessage()); + } + } + +/* + * CompositeExpression + * Allow most flexible parsing, restrict by examining + * type of returned node + */ + final public AstCompositeExpression CompositeExpression() throws ParseException { + /*@bgen(jjtree) CompositeExpression */ + AstCompositeExpression jjtn000 = new AstCompositeExpression(JJTCOMPOSITEEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + label_1: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LITERAL_EXPRESSION: + case START_DYNAMIC_EXPRESSION: + case START_DEFERRED_EXPRESSION: + ; + break; + default: + jj_la1[0] = jj_gen; + break label_1; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case START_DEFERRED_EXPRESSION: + DeferredExpression(); + break; + case START_DYNAMIC_EXPRESSION: + DynamicExpression(); + break; + case LITERAL_EXPRESSION: + LiteralExpression(); + break; + default: + jj_la1[1] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(0); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + {if (true) return jjtn000;} + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + throw new Error("Missing return statement in function"); + } + +/* + * LiteralExpression + * Non-EL Expression blocks + */ + final public void LiteralExpression() throws ParseException { + /*@bgen(jjtree) LiteralExpression */ + AstLiteralExpression jjtn000 = new AstLiteralExpression(JJTLITERALEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000);Token t = null; + try { + t = jj_consume_token(LITERAL_EXPRESSION); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setImage(t.image); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * DeferredExpression + * #{..} Expressions + */ + final public void DeferredExpression() throws ParseException { + /*@bgen(jjtree) DeferredExpression */ + AstDeferredExpression jjtn000 = new AstDeferredExpression(JJTDEFERREDEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(START_DEFERRED_EXPRESSION); + Expression(); + jj_consume_token(RCURL); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * DynamicExpression + * ${..} Expressions + */ + final public void DynamicExpression() throws ParseException { + /*@bgen(jjtree) DynamicExpression */ + AstDynamicExpression jjtn000 = new AstDynamicExpression(JJTDYNAMICEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(START_DYNAMIC_EXPRESSION); + Expression(); + jj_consume_token(RCURL); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * Expression + * EL Expression Language Root + */ + final public void Expression() throws ParseException { + SemiColon(); + } + +/* + * SemiColon + */ + final public void SemiColon() throws ParseException { + Assignment(); + label_2: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + ; + break; + default: + jj_la1[2] = jj_gen; + break label_2; + } + jj_consume_token(SEMICOLON); + AstSemiColon jjtn001 = new AstSemiColon(JJTSEMICOLON); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Assignment(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + } + } + +/* + * Assignment + * For '=', right associatve, then LambdaExpression or Choice or Assignment + */ + final public void Assignment() throws ParseException { + if (jj_2_1(3)) { + LambdaExpression(); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + Choice(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ASSIGN: + jj_consume_token(ASSIGN); + AstAssign jjtn001 = new AstAssign(JJTASSIGN); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Assignment(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + break; + default: + jj_la1[3] = jj_gen; + ; + } + break; + default: + jj_la1[4] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + +/* + * LambdaExpression + */ + final public void LambdaExpression() throws ParseException { + /*@bgen(jjtree) LambdaExpression */ + AstLambdaExpression jjtn000 = new AstLambdaExpression(JJTLAMBDAEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + LambdaParameters(); + jj_consume_token(ARROW); + if (jj_2_2(3)) { + LambdaExpression(); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + Choice(); + break; + default: + jj_la1[5] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + final public void LambdaParameters() throws ParseException { + /*@bgen(jjtree) LambdaParameters */ + AstLambdaParameters jjtn000 = new AstLambdaParameters(JJTLAMBDAPARAMETERS); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENTIFIER: + Identifier(); + break; + case LPAREN: + jj_consume_token(LPAREN); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENTIFIER: + Identifier(); + label_3: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[6] = jj_gen; + break label_3; + } + jj_consume_token(COMMA); + Identifier(); + } + break; + default: + jj_la1[7] = jj_gen; + ; + } + jj_consume_token(RPAREN); + break; + default: + jj_la1[8] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * Choice + * For Choice markup a ? b : c, right associative + */ + final public void Choice() throws ParseException { + Or(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case QUESTIONMARK: + jj_consume_token(QUESTIONMARK); + Choice(); + jj_consume_token(COLON); + AstChoice jjtn001 = new AstChoice(JJTCHOICE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Choice(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 3); + } + } + break; + default: + jj_la1[9] = jj_gen; + ; + } + } + +/* + * Or + * For 'or' '||', then And + */ + final public void Or() throws ParseException { + And(); + label_4: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case OR0: + case OR1: + ; + break; + default: + jj_la1[10] = jj_gen; + break label_4; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case OR0: + jj_consume_token(OR0); + break; + case OR1: + jj_consume_token(OR1); + break; + default: + jj_la1[11] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstOr jjtn001 = new AstOr(JJTOR); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + And(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + } + } + +/* + * And + * For 'and' '&&', then Equality + */ + final public void And() throws ParseException { + Equality(); + label_5: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case AND0: + case AND1: + ; + break; + default: + jj_la1[12] = jj_gen; + break label_5; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case AND0: + jj_consume_token(AND0); + break; + case AND1: + jj_consume_token(AND1); + break; + default: + jj_la1[13] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstAnd jjtn001 = new AstAnd(JJTAND); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Equality(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + } + } + +/* + * Equality + * For '==' 'eq' '!=' 'ne', then Compare + */ + final public void Equality() throws ParseException { + Compare(); + label_6: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case EQ0: + case EQ1: + case NE0: + case NE1: + ; + break; + default: + jj_la1[14] = jj_gen; + break label_6; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case EQ0: + case EQ1: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case EQ0: + jj_consume_token(EQ0); + break; + case EQ1: + jj_consume_token(EQ1); + break; + default: + jj_la1[15] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstEqual jjtn001 = new AstEqual(JJTEQUAL); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Compare(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + break; + case NE0: + case NE1: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case NE0: + jj_consume_token(NE0); + break; + case NE1: + jj_consume_token(NE1); + break; + default: + jj_la1[16] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstNotEqual jjtn002 = new AstNotEqual(JJTNOTEQUAL); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + Compare(); + } catch (Throwable jjte002) { + if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte002;} + } + if (jjte002 instanceof ParseException) { + {if (true) throw (ParseException)jjte002;} + } + {if (true) throw (Error)jjte002;} + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + } + } + break; + default: + jj_la1[17] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + +/* + * Compare + * For a bunch of them, then Math + */ + final public void Compare() throws ParseException { + Concatenation(); + label_7: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case GT0: + case GT1: + case LT0: + case LT1: + case GE0: + case GE1: + case LE0: + case LE1: + ; + break; + default: + jj_la1[18] = jj_gen; + break label_7; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LT0: + case LT1: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LT0: + jj_consume_token(LT0); + break; + case LT1: + jj_consume_token(LT1); + break; + default: + jj_la1[19] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstLessThan jjtn001 = new AstLessThan(JJTLESSTHAN); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Concatenation(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + break; + case GT0: + case GT1: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case GT0: + jj_consume_token(GT0); + break; + case GT1: + jj_consume_token(GT1); + break; + default: + jj_la1[20] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstGreaterThan jjtn002 = new AstGreaterThan(JJTGREATERTHAN); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + Concatenation(); + } catch (Throwable jjte002) { + if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte002;} + } + if (jjte002 instanceof ParseException) { + {if (true) throw (ParseException)jjte002;} + } + {if (true) throw (Error)jjte002;} + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + } + } + break; + case LE0: + case LE1: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LE0: + jj_consume_token(LE0); + break; + case LE1: + jj_consume_token(LE1); + break; + default: + jj_la1[21] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstLessThanEqual jjtn003 = new AstLessThanEqual(JJTLESSTHANEQUAL); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + try { + Concatenation(); + } catch (Throwable jjte003) { + if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte003;} + } + if (jjte003 instanceof ParseException) { + {if (true) throw (ParseException)jjte003;} + } + {if (true) throw (Error)jjte003;} + } finally { + if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 2); + } + } + break; + case GE0: + case GE1: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case GE0: + jj_consume_token(GE0); + break; + case GE1: + jj_consume_token(GE1); + break; + default: + jj_la1[22] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstGreaterThanEqual jjtn004 = new AstGreaterThanEqual(JJTGREATERTHANEQUAL); + boolean jjtc004 = true; + jjtree.openNodeScope(jjtn004); + try { + Concatenation(); + } catch (Throwable jjte004) { + if (jjtc004) { + jjtree.clearNodeScope(jjtn004); + jjtc004 = false; + } else { + jjtree.popNode(); + } + if (jjte004 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte004;} + } + if (jjte004 instanceof ParseException) { + {if (true) throw (ParseException)jjte004;} + } + {if (true) throw (Error)jjte004;} + } finally { + if (jjtc004) { + jjtree.closeNodeScope(jjtn004, 2); + } + } + break; + default: + jj_la1[23] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + +/* + * Concatenation + * For '&', then Math() + */ + final public void Concatenation() throws ParseException { + Math(); + label_8: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case CONCAT: + ; + break; + default: + jj_la1[24] = jj_gen; + break label_8; + } + jj_consume_token(CONCAT); + AstConcat jjtn001 = new AstConcat(JJTCONCAT); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Math(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + } + } + +/* + * Math + * For '+' '-', then Multiplication + */ + final public void Math() throws ParseException { + Multiplication(); + label_9: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case MINUS: + ; + break; + default: + jj_la1[25] = jj_gen; + break label_9; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + jj_consume_token(PLUS); + AstPlus jjtn001 = new AstPlus(JJTPLUS); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Multiplication(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + break; + case MINUS: + jj_consume_token(MINUS); + AstMinus jjtn002 = new AstMinus(JJTMINUS); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + Multiplication(); + } catch (Throwable jjte002) { + if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte002;} + } + if (jjte002 instanceof ParseException) { + {if (true) throw (ParseException)jjte002;} + } + {if (true) throw (Error)jjte002;} + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + } + } + break; + default: + jj_la1[26] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + +/* + * Multiplication + * For a bunch of them, then Unary + */ + final public void Multiplication() throws ParseException { + Unary(); + label_10: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case MULT: + case DIV0: + case DIV1: + case MOD0: + case MOD1: + ; + break; + default: + jj_la1[27] = jj_gen; + break label_10; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case MULT: + jj_consume_token(MULT); + AstMult jjtn001 = new AstMult(JJTMULT); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Unary(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + break; + case DIV0: + case DIV1: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DIV0: + jj_consume_token(DIV0); + break; + case DIV1: + jj_consume_token(DIV1); + break; + default: + jj_la1[28] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstDiv jjtn002 = new AstDiv(JJTDIV); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + Unary(); + } catch (Throwable jjte002) { + if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte002;} + } + if (jjte002 instanceof ParseException) { + {if (true) throw (ParseException)jjte002;} + } + {if (true) throw (Error)jjte002;} + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + } + } + break; + case MOD0: + case MOD1: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case MOD0: + jj_consume_token(MOD0); + break; + case MOD1: + jj_consume_token(MOD1); + break; + default: + jj_la1[29] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstMod jjtn003 = new AstMod(JJTMOD); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + try { + Unary(); + } catch (Throwable jjte003) { + if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte003;} + } + if (jjte003 instanceof ParseException) { + {if (true) throw (ParseException)jjte003;} + } + {if (true) throw (Error)jjte003;} + } finally { + if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 2); + } + } + break; + default: + jj_la1[30] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + +/* + * Unary + * For '-' '!' 'not' 'empty', then Value + */ + final public void Unary() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case MINUS: + jj_consume_token(MINUS); + AstNegative jjtn001 = new AstNegative(JJTNEGATIVE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Unary(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, true); + } + } + break; + case NOT0: + case NOT1: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case NOT0: + jj_consume_token(NOT0); + break; + case NOT1: + jj_consume_token(NOT1); + break; + default: + jj_la1[31] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstNot jjtn002 = new AstNot(JJTNOT); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + Unary(); + } catch (Throwable jjte002) { + if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte002;} + } + if (jjte002 instanceof ParseException) { + {if (true) throw (ParseException)jjte002;} + } + {if (true) throw (Error)jjte002;} + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, true); + } + } + break; + case EMPTY: + jj_consume_token(EMPTY); + AstEmpty jjtn003 = new AstEmpty(JJTEMPTY); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + try { + Unary(); + } catch (Throwable jjte003) { + if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte003;} + } + if (jjte003 instanceof ParseException) { + {if (true) throw (ParseException)jjte003;} + } + {if (true) throw (Error)jjte003;} + } finally { + if (jjtc003) { + jjtree.closeNodeScope(jjtn003, true); + } + } + break; + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case IDENTIFIER: + Value(); + break; + default: + jj_la1[32] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + +/* + * Value + * Defines Prefix plus zero or more Suffixes + */ + final public void Value() throws ParseException { + AstValue jjtn001 = new AstValue(JJTVALUE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + ValuePrefix(); + label_11: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DOT: + case LBRACK: + ; + break; + default: + jj_la1[33] = jj_gen; + break label_11; + } + ValueSuffix(); + } + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte001;} + } + if (jjte001 instanceof ParseException) { + {if (true) throw (ParseException)jjte001;} + } + {if (true) throw (Error)jjte001;} + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, jjtree.nodeArity() > 1); + } + } + } + +/* + * ValuePrefix + * For Literals, Variables, and Functions + */ + final public void ValuePrefix() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + Literal(); + break; + case START_MAP: + case LPAREN: + case LBRACK: + case IDENTIFIER: + NonLiteral(); + break; + default: + jj_la1[34] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + +/* + * ValueSuffix + * Either dot or bracket notation + */ + final public void ValueSuffix() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DOT: + DotSuffix(); + break; + case LBRACK: + BracketSuffix(); + break; + default: + jj_la1[35] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + +/* + * DotSuffix + * Dot Property and Dot Method + */ + final public void DotSuffix() throws ParseException { + /*@bgen(jjtree) DotSuffix */ + AstDotSuffix jjtn000 = new AstDotSuffix(JJTDOTSUFFIX); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000);Token t = null; + try { + jj_consume_token(DOT); + t = jj_consume_token(IDENTIFIER); + jjtn000.setImage(t.image); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LPAREN: + MethodArguments(); + break; + default: + jj_la1[36] = jj_gen; + ; + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * BracketSuffix + * Sub Expression Suffix + */ + final public void BracketSuffix() throws ParseException { + /*@bgen(jjtree) BracketSuffix */ + AstBracketSuffix jjtn000 = new AstBracketSuffix(JJTBRACKETSUFFIX); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(LBRACK); + Expression(); + jj_consume_token(RBRACK); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LPAREN: + MethodArguments(); + break; + default: + jj_la1[37] = jj_gen; + ; + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * MethodArguments + */ + final public void MethodArguments() throws ParseException { + /*@bgen(jjtree) MethodArguments */ + AstMethodArguments jjtn000 = new AstMethodArguments(JJTMETHODARGUMENTS); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(LPAREN); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + Expression(); + label_12: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[38] = jj_gen; + break label_12; + } + jj_consume_token(COMMA); + Expression(); + } + break; + default: + jj_la1[39] = jj_gen; + ; + } + jj_consume_token(RPAREN); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * Parenthesized Lambda Expression, with optional invokation + */ + final public void LambdaExpressionOrCall() throws ParseException { + /*@bgen(jjtree) LambdaExpression */ + AstLambdaExpression jjtn000 = new AstLambdaExpression(JJTLAMBDAEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(LPAREN); + LambdaParameters(); + jj_consume_token(ARROW); + if (jj_2_3(3)) { + LambdaExpression(); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + Choice(); + break; + default: + jj_la1[40] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(RPAREN); + label_13: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LPAREN: + ; + break; + default: + jj_la1[41] = jj_gen; + break label_13; + } + MethodArguments(); + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * NonLiteral + * For Grouped Operations, Identifiers, and Functions + */ + final public void NonLiteral() throws ParseException { + if (jj_2_4(4)) { + LambdaExpressionOrCall(); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LPAREN: + jj_consume_token(LPAREN); + Expression(); + jj_consume_token(RPAREN); + break; + default: + jj_la1[42] = jj_gen; + if (jj_2_5(4)) { + Function(); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENTIFIER: + Identifier(); + break; + case START_MAP: + MapData(); + break; + case LBRACK: + ListData(); + break; + default: + jj_la1[43] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + } + } + + final public void MapData() throws ParseException { + /*@bgen(jjtree) MapData */ + AstMapData jjtn000 = new AstMapData(JJTMAPDATA); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(START_MAP); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + MapEntry(); + label_14: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[44] = jj_gen; + break label_14; + } + jj_consume_token(COMMA); + MapEntry(); + } + break; + default: + jj_la1[45] = jj_gen; + ; + } + jj_consume_token(RCURL); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + final public void MapEntry() throws ParseException { + /*@bgen(jjtree) MapEntry */ + AstMapEntry jjtn000 = new AstMapEntry(JJTMAPENTRY); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + Expression(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COLON: + jj_consume_token(COLON); + Expression(); + break; + default: + jj_la1[46] = jj_gen; + ; + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + final public void ListData() throws ParseException { + /*@bgen(jjtree) ListData */ + AstListData jjtn000 = new AstListData(JJTLISTDATA); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(LBRACK); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + Expression(); + label_15: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[47] = jj_gen; + break label_15; + } + jj_consume_token(COMMA); + Expression(); + } + break; + default: + jj_la1[48] = jj_gen; + ; + } + jj_consume_token(RBRACK); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * Identifier + * Java Language Identifier + */ + final public void Identifier() throws ParseException { + /*@bgen(jjtree) Identifier */ + AstIdentifier jjtn000 = new AstIdentifier(JJTIDENTIFIER); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000);Token t = null; + try { + t = jj_consume_token(IDENTIFIER); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setImage(t.image); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * Function + * Namespace:Name(a,b,c) + */ + final public void Function() throws ParseException { + /*@bgen(jjtree) Function */ + AstFunction jjtn000 = new AstFunction(JJTFUNCTION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000);Token t0 = null; + Token t1 = null; + try { + t0 = jj_consume_token(IDENTIFIER); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COLON: + jj_consume_token(COLON); + t1 = jj_consume_token(IDENTIFIER); + break; + default: + jj_la1[49] = jj_gen; + ; + } + if (t1 != null) { + jjtn000.setPrefix(t0.image); + jjtn000.setLocalName(t1.image); + } else { + jjtn000.setLocalName(t0.image); + } + label_16: + while (true) { + MethodArguments(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LPAREN: + ; + break; + default: + jj_la1[50] = jj_gen; + break label_16; + } + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * Literal + * Reserved Keywords + */ + final public void Literal() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case TRUE: + case FALSE: + Boolean(); + break; + case FLOATING_POINT_LITERAL: + FloatingPoint(); + break; + case INTEGER_LITERAL: + Integer(); + break; + case STRING_LITERAL: + String(); + break; + case NULL: + Null(); + break; + default: + jj_la1[51] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + +/* + * Boolean + * For 'true' 'false' + */ + final public void Boolean() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case TRUE: + AstTrue jjtn001 = new AstTrue(JJTTRUE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + jj_consume_token(TRUE); + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, true); + } + } + break; + case FALSE: + AstFalse jjtn002 = new AstFalse(JJTFALSE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + jj_consume_token(FALSE); + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, true); + } + } + break; + default: + jj_la1[52] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + +/* + * FloatinPoint + * For Decimal and Floating Point Literals + */ + final public void FloatingPoint() throws ParseException { + /*@bgen(jjtree) FloatingPoint */ + AstFloatingPoint jjtn000 = new AstFloatingPoint(JJTFLOATINGPOINT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000);Token t = null; + try { + t = jj_consume_token(FLOATING_POINT_LITERAL); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setImage(t.image); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * Integer + * For Simple Numeric Literals + */ + final public void Integer() throws ParseException { + /*@bgen(jjtree) Integer */ + AstInteger jjtn000 = new AstInteger(JJTINTEGER); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000);Token t = null; + try { + t = jj_consume_token(INTEGER_LITERAL); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setImage(t.image); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * String + * For Quoted Literals + */ + final public void String() throws ParseException { + /*@bgen(jjtree) String */ + AstString jjtn000 = new AstString(JJTSTRING); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000);Token t = null; + try { + t = jj_consume_token(STRING_LITERAL); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setImage(t.image); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + +/* + * Null + * For 'null' + */ + final public void Null() throws ParseException { + /*@bgen(jjtree) Null */ + AstNull jjtn000 = new AstNull(JJTNULL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(NULL); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + private boolean jj_2_1(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_1(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(0, xla); } + } + + private boolean jj_2_2(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_2(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(1, xla); } + } + + private boolean jj_2_3(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_3(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(2, xla); } + } + + private boolean jj_2_4(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_4(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(3, xla); } + } + + private boolean jj_2_5(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_5(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(4, xla); } + } + + private boolean jj_3R_89() { + if (jj_scan_token(LBRACK)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_102()) jj_scanpos = xsp; + if (jj_scan_token(RBRACK)) return true; + return false; + } + + private boolean jj_3R_33() { + if (jj_scan_token(COMMA)) return true; + return false; + } + + private boolean jj_3R_31() { + if (jj_3R_34()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_49()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_47() { + if (jj_scan_token(QUESTIONMARK)) return true; + return false; + } + + private boolean jj_3R_103() { + if (jj_3R_35()) return true; + return false; + } + + private boolean jj_3R_101() { + if (jj_3R_103()) return true; + return false; + } + + private boolean jj_3R_27() { + if (jj_3R_31()) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_47()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_79() { + if (jj_3R_89()) return true; + return false; + } + + private boolean jj_3R_88() { + if (jj_scan_token(START_MAP)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_101()) jj_scanpos = xsp; + if (jj_scan_token(RCURL)) return true; + return false; + } + + private boolean jj_3R_78() { + if (jj_3R_88()) return true; + return false; + } + + private boolean jj_3R_77() { + if (jj_3R_29()) return true; + return false; + } + + private boolean jj_3_5() { + if (jj_3R_19()) return true; + return false; + } + + private boolean jj_3R_30() { + if (jj_3R_29()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_33()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_76() { + if (jj_scan_token(LPAREN)) return true; + if (jj_3R_35()) return true; + return false; + } + + private boolean jj_3R_36() { + if (jj_scan_token(COMMA)) return true; + return false; + } + + private boolean jj_3_4() { + if (jj_3R_18()) return true; + return false; + } + + private boolean jj_3R_69() { + Token xsp; + xsp = jj_scanpos; + if (jj_3_4()) { + jj_scanpos = xsp; + if (jj_3R_76()) { + jj_scanpos = xsp; + if (jj_3_5()) { + jj_scanpos = xsp; + if (jj_3R_77()) { + jj_scanpos = xsp; + if (jj_3R_78()) { + jj_scanpos = xsp; + if (jj_3R_79()) return true; + } + } + } + } + } + return false; + } + + private boolean jj_3R_26() { + if (jj_scan_token(LPAREN)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_30()) jj_scanpos = xsp; + if (jj_scan_token(RPAREN)) return true; + return false; + } + + private boolean jj_3R_20() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_25()) { + jj_scanpos = xsp; + if (jj_3R_26()) return true; + } + return false; + } + + private boolean jj_3R_25() { + if (jj_3R_29()) return true; + return false; + } + + private boolean jj_3R_45() { + if (jj_scan_token(ASSIGN)) return true; + return false; + } + + private boolean jj_3_2() { + if (jj_3R_17()) return true; + return false; + } + + private boolean jj_3_3() { + if (jj_3R_17()) return true; + return false; + } + + private boolean jj_3R_17() { + if (jj_3R_20()) return true; + if (jj_scan_token(ARROW)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3_2()) { + jj_scanpos = xsp; + if (jj_3R_21()) return true; + } + return false; + } + + private boolean jj_3R_32() { + if (jj_3R_35()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_36()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_41() { + if (jj_scan_token(SEMICOLON)) return true; + return false; + } + + private boolean jj_3R_18() { + if (jj_scan_token(LPAREN)) return true; + if (jj_3R_20()) return true; + if (jj_scan_token(ARROW)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3_3()) { + jj_scanpos = xsp; + if (jj_3R_22()) return true; + } + return false; + } + + private boolean jj_3R_43() { + if (jj_3R_27()) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_45()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_40() { + Token xsp; + xsp = jj_scanpos; + if (jj_3_1()) { + jj_scanpos = xsp; + if (jj_3R_43()) return true; + } + return false; + } + + private boolean jj_3_1() { + if (jj_3R_17()) return true; + return false; + } + + private boolean jj_3R_28() { + if (jj_scan_token(LPAREN)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_32()) jj_scanpos = xsp; + if (jj_scan_token(RPAREN)) return true; + return false; + } + + private boolean jj_3R_38() { + if (jj_3R_40()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_41()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_100() { + if (jj_scan_token(LBRACK)) return true; + return false; + } + + private boolean jj_3R_35() { + if (jj_3R_38()) return true; + return false; + } + + private boolean jj_3R_98() { + if (jj_3R_100()) return true; + return false; + } + + private boolean jj_3R_99() { + if (jj_scan_token(DOT)) return true; + return false; + } + + private boolean jj_3R_97() { + if (jj_3R_99()) return true; + return false; + } + + private boolean jj_3R_96() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_97()) { + jj_scanpos = xsp; + if (jj_3R_98()) return true; + } + return false; + } + + private boolean jj_3R_95() { + if (jj_3R_96()) return true; + return false; + } + + private boolean jj_3R_62() { + if (jj_3R_69()) return true; + return false; + } + + private boolean jj_3R_57() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_61()) { + jj_scanpos = xsp; + if (jj_3R_62()) return true; + } + return false; + } + + private boolean jj_3R_61() { + if (jj_3R_68()) return true; + return false; + } + + private boolean jj_3R_55() { + if (jj_3R_57()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_95()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_53() { + if (jj_3R_55()) return true; + return false; + } + + private boolean jj_3R_52() { + if (jj_scan_token(EMPTY)) return true; + if (jj_3R_48()) return true; + return false; + } + + private boolean jj_3R_51() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(39)) { + jj_scanpos = xsp; + if (jj_scan_token(40)) return true; + } + if (jj_3R_48()) return true; + return false; + } + + private boolean jj_3R_87() { + if (jj_scan_token(NULL)) return true; + return false; + } + + private boolean jj_3R_48() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_50()) { + jj_scanpos = xsp; + if (jj_3R_51()) { + jj_scanpos = xsp; + if (jj_3R_52()) { + jj_scanpos = xsp; + if (jj_3R_53()) return true; + } + } + } + return false; + } + + private boolean jj_3R_50() { + if (jj_scan_token(MINUS)) return true; + if (jj_3R_48()) return true; + return false; + } + + private boolean jj_3R_86() { + if (jj_scan_token(STRING_LITERAL)) return true; + return false; + } + + private boolean jj_3R_92() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(53)) { + jj_scanpos = xsp; + if (jj_scan_token(54)) return true; + } + return false; + } + + private boolean jj_3R_91() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(51)) { + jj_scanpos = xsp; + if (jj_scan_token(52)) return true; + } + return false; + } + + private boolean jj_3R_90() { + if (jj_scan_token(MULT)) return true; + return false; + } + + private boolean jj_3R_80() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_90()) { + jj_scanpos = xsp; + if (jj_3R_91()) { + jj_scanpos = xsp; + if (jj_3R_92()) return true; + } + } + return false; + } + + private boolean jj_3R_85() { + if (jj_scan_token(INTEGER_LITERAL)) return true; + return false; + } + + private boolean jj_3R_46() { + if (jj_3R_48()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_80()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_84() { + if (jj_scan_token(FLOATING_POINT_LITERAL)) return true; + return false; + } + + private boolean jj_3R_82() { + if (jj_scan_token(MINUS)) return true; + return false; + } + + private boolean jj_3R_70() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_81()) { + jj_scanpos = xsp; + if (jj_3R_82()) return true; + } + return false; + } + + private boolean jj_3R_81() { + if (jj_scan_token(PLUS)) return true; + return false; + } + + private boolean jj_3R_94() { + if (jj_scan_token(FALSE)) return true; + return false; + } + + private boolean jj_3R_93() { + if (jj_scan_token(TRUE)) return true; + return false; + } + + private boolean jj_3R_83() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_93()) { + jj_scanpos = xsp; + if (jj_3R_94()) return true; + } + return false; + } + + private boolean jj_3R_44() { + if (jj_3R_46()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_70()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_63() { + if (jj_scan_token(CONCAT)) return true; + return false; + } + + private boolean jj_3R_75() { + if (jj_3R_87()) return true; + return false; + } + + private boolean jj_3R_74() { + if (jj_3R_86()) return true; + return false; + } + + private boolean jj_3R_73() { + if (jj_3R_85()) return true; + return false; + } + + private boolean jj_3R_72() { + if (jj_3R_84()) return true; + return false; + } + + private boolean jj_3R_42() { + if (jj_3R_44()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_63()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_67() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(31)) { + jj_scanpos = xsp; + if (jj_scan_token(32)) return true; + } + return false; + } + + private boolean jj_3R_68() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_71()) { + jj_scanpos = xsp; + if (jj_3R_72()) { + jj_scanpos = xsp; + if (jj_3R_73()) { + jj_scanpos = xsp; + if (jj_3R_74()) { + jj_scanpos = xsp; + if (jj_3R_75()) return true; + } + } + } + } + return false; + } + + private boolean jj_3R_71() { + if (jj_3R_83()) return true; + return false; + } + + private boolean jj_3R_66() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(33)) { + jj_scanpos = xsp; + if (jj_scan_token(34)) return true; + } + return false; + } + + private boolean jj_3R_23() { + if (jj_scan_token(COLON)) return true; + if (jj_scan_token(IDENTIFIER)) return true; + return false; + } + + private boolean jj_3R_65() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(27)) { + jj_scanpos = xsp; + if (jj_scan_token(28)) return true; + } + return false; + } + + private boolean jj_3R_58() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_64()) { + jj_scanpos = xsp; + if (jj_3R_65()) { + jj_scanpos = xsp; + if (jj_3R_66()) { + jj_scanpos = xsp; + if (jj_3R_67()) return true; + } + } + } + return false; + } + + private boolean jj_3R_64() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(29)) { + jj_scanpos = xsp; + if (jj_scan_token(30)) return true; + } + return false; + } + + private boolean jj_3R_24() { + if (jj_3R_28()) return true; + return false; + } + + private boolean jj_3R_39() { + if (jj_3R_42()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_58()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_60() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(37)) { + jj_scanpos = xsp; + if (jj_scan_token(38)) return true; + } + return false; + } + + private boolean jj_3R_19() { + if (jj_scan_token(IDENTIFIER)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_23()) jj_scanpos = xsp; + if (jj_3R_24()) return true; + while (true) { + xsp = jj_scanpos; + if (jj_3R_24()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_56() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_59()) { + jj_scanpos = xsp; + if (jj_3R_60()) return true; + } + return false; + } + + private boolean jj_3R_59() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(35)) { + jj_scanpos = xsp; + if (jj_scan_token(36)) return true; + } + return false; + } + + private boolean jj_3R_54() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(41)) { + jj_scanpos = xsp; + if (jj_scan_token(42)) return true; + } + return false; + } + + private boolean jj_3R_37() { + if (jj_3R_39()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_56()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_29() { + if (jj_scan_token(IDENTIFIER)) return true; + return false; + } + + private boolean jj_3R_34() { + if (jj_3R_37()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_54()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_102() { + if (jj_3R_35()) return true; + return false; + } + + private boolean jj_3R_21() { + if (jj_3R_27()) return true; + return false; + } + + private boolean jj_3R_49() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(43)) { + jj_scanpos = xsp; + if (jj_scan_token(44)) return true; + } + return false; + } + + private boolean jj_3R_22() { + if (jj_3R_27()) return true; + return false; + } + + /** Generated Token Manager. */ + public ELParserTokenManager token_source; + SimpleCharStream jj_input_stream; + /** Current token. */ + public Token token; + /** Next token. */ + public Token jj_nt; + private int jj_ntk; + private Token jj_scanpos, jj_lastpos; + private int jj_la; + private int jj_gen; + final private int[] jj_la1 = new int[53]; + static private int[] jj_la1_0; + static private int[] jj_la1_1; + static { + jj_la1_init_0(); + jj_la1_init_1(); + } + private static void jj_la1_init_0() { + jj_la1_0 = new int[] {0xe,0xe,0x4000000,0x0,0x575a00,0x575a00,0x2000000,0x0,0x100000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000000,0x60000000,0x18000000,0x0,0x80000000,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x575a00,0x480000,0x575a00,0x480000,0x100000,0x100000,0x2000000,0x575a00,0x575a00,0x100000,0x100000,0x400200,0x2000000,0x575a00,0x1000000,0x2000000,0x575a00,0x1000000,0x100000,0x75800,0x30000,}; + } + private static void jj_la1_init_1() { + jj_la1_1 = new int[] {0x0,0x0,0x0,0x1000000,0x4022180,0x4022180,0x0,0x4000000,0x4000000,0x40000,0x1800,0x1800,0x600,0x600,0x78,0x18,0x60,0x78,0x7,0x0,0x0,0x6,0x1,0x7,0x800000,0x30000,0x30000,0x788000,0x180000,0x600000,0x788000,0x180,0x4022180,0x0,0x4000000,0x0,0x0,0x0,0x0,0x4022180,0x4022180,0x0,0x0,0x4000000,0x0,0x4022180,0x0,0x0,0x4022180,0x0,0x0,0x0,0x0,}; + } + final private JJCalls[] jj_2_rtns = new JJCalls[5]; + private boolean jj_rescan = false; + private int jj_gc = 0; + + /** Constructor with InputStream. */ + public ELParser(java.io.InputStream stream) { + this(stream, null); + } + /** Constructor with InputStream and supplied encoding */ + public ELParser(java.io.InputStream stream, String encoding) { + try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } + token_source = new ELParserTokenManager(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 53; i++) jj_la1[i] = -1; + for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream stream) { + ReInit(stream, null); + } + /** Reinitialise. */ + public void ReInit(java.io.InputStream stream, String encoding) { + try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } + token_source.ReInit(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jjtree.reset(); + jj_gen = 0; + for (int i = 0; i < 53; i++) jj_la1[i] = -1; + for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); + } + + /** Constructor. */ + public ELParser(java.io.Reader stream) { + jj_input_stream = new SimpleCharStream(stream, 1, 1); + token_source = new ELParserTokenManager(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 53; i++) jj_la1[i] = -1; + for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader stream) { + jj_input_stream.ReInit(stream, 1, 1); + token_source.ReInit(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jjtree.reset(); + jj_gen = 0; + for (int i = 0; i < 53; i++) jj_la1[i] = -1; + for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); + } + + /** Constructor with generated Token Manager. */ + public ELParser(ELParserTokenManager tm) { + token_source = tm; + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 53; i++) jj_la1[i] = -1; + for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); + } + + /** Reinitialise. */ + public void ReInit(ELParserTokenManager tm) { + token_source = tm; + token = new Token(); + jj_ntk = -1; + jjtree.reset(); + jj_gen = 0; + for (int i = 0; i < 53; i++) jj_la1[i] = -1; + for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); + } + + private Token jj_consume_token(int kind) throws ParseException { + Token oldToken; + if ((oldToken = token).next != null) token = token.next; + else token = token.next = token_source.getNextToken(); + jj_ntk = -1; + if (token.kind == kind) { + jj_gen++; + if (++jj_gc > 100) { + jj_gc = 0; + for (int i = 0; i < jj_2_rtns.length; i++) { + JJCalls c = jj_2_rtns[i]; + while (c != null) { + if (c.gen < jj_gen) c.first = null; + c = c.next; + } + } + } + return token; + } + token = oldToken; + jj_kind = kind; + throw generateParseException(); + } + + static private final class LookaheadSuccess extends java.lang.Error { } + final private LookaheadSuccess jj_ls = new LookaheadSuccess(); + private boolean jj_scan_token(int kind) { + if (jj_scanpos == jj_lastpos) { + jj_la--; + if (jj_scanpos.next == null) { + jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken(); + } else { + jj_lastpos = jj_scanpos = jj_scanpos.next; + } + } else { + jj_scanpos = jj_scanpos.next; + } + if (jj_rescan) { + int i = 0; Token tok = token; + while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; } + if (tok != null) jj_add_error_token(kind, i); + } + if (jj_scanpos.kind != kind) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls; + return false; + } + + +/** Get the next Token. */ + final public Token getNextToken() { + if (token.next != null) token = token.next; + else token = token.next = token_source.getNextToken(); + jj_ntk = -1; + jj_gen++; + return token; + } + +/** Get the specific Token. */ + final public Token getToken(int index) { + Token t = token; + for (int i = 0; i < index; i++) { + if (t.next != null) t = t.next; + else t = t.next = token_source.getNextToken(); + } + return t; + } + + private int jj_ntk() { + if ((jj_nt=token.next) == null) + return (jj_ntk = (token.next=token_source.getNextToken()).kind); + else + return (jj_ntk = jj_nt.kind); + } + + private java.util.List jj_expentries = new java.util.ArrayList(); + private int[] jj_expentry; + private int jj_kind = -1; + private int[] jj_lasttokens = new int[100]; + private int jj_endpos; + + private void jj_add_error_token(int kind, int pos) { + if (pos >= 100) return; + if (pos == jj_endpos + 1) { + jj_lasttokens[jj_endpos++] = kind; + } else if (jj_endpos != 0) { + jj_expentry = new int[jj_endpos]; + for (int i = 0; i < jj_endpos; i++) { + jj_expentry[i] = jj_lasttokens[i]; + } + jj_entries_loop: for (java.util.Iterator it = jj_expentries.iterator(); it.hasNext();) { + int[] oldentry = (int[])(it.next()); + if (oldentry.length == jj_expentry.length) { + for (int i = 0; i < jj_expentry.length; i++) { + if (oldentry[i] != jj_expentry[i]) { + continue jj_entries_loop; + } + } + jj_expentries.add(jj_expentry); + break jj_entries_loop; + } + } + if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind; + } + } + + /** Generate ParseException. */ + public ParseException generateParseException() { + jj_expentries.clear(); + boolean[] la1tokens = new boolean[63]; + if (jj_kind >= 0) { + la1tokens[jj_kind] = true; + jj_kind = -1; + } + for (int i = 0; i < 53; i++) { + if (jj_la1[i] == jj_gen) { + for (int j = 0; j < 32; j++) { + if ((jj_la1_0[i] & (1< jj_gen) { + jj_la = p.arg; jj_lastpos = jj_scanpos = p.first; + switch (i) { + case 0: jj_3_1(); break; + case 1: jj_3_2(); break; + case 2: jj_3_3(); break; + case 3: jj_3_4(); break; + case 4: jj_3_5(); break; + } + } + p = p.next; + } while (p != null); + } catch(LookaheadSuccess ls) { } + } + jj_rescan = false; + } + + private void jj_save(int index, int xla) { + JJCalls p = jj_2_rtns[index]; + while (p.gen > jj_gen) { + if (p.next == null) { p = p.next = new JJCalls(); break; } + p = p.next; + } + p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla; + } + + static final class JJCalls { + int gen; + Token first; + int arg; + JJCalls next; + } + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParser.jjt b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParser.jjt new file mode 100644 index 000000000..cc271847f --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParser.jjt @@ -0,0 +1,597 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +/* + Author: Jacob Hookom + Email: jacob at hookom.net + + Author: Kin-man Chung (EL 2.2 and EL 3.0) +*/ + +/* == Option Declaration == */ +options +{ + STATIC=false; + NODE_PREFIX="Ast"; + VISITOR_EXCEPTION="javax.el.ELException"; + VISITOR=false; + MULTI=true; + NODE_DEFAULT_VOID=true; + JAVA_UNICODE_ESCAPE=false; + UNICODE_INPUT=true; + BUILD_NODE_FILES=true; +} + +/* == Parser Declaration == */ +PARSER_BEGIN( ELParser ) +package com.sun.el.parser; +import java.io.StringReader; +import javax.el.ELException; +public class ELParser +{ + public static Node parse(String ref) throws ELException + { + try { + return (new ELParser(new StringReader(ref))).CompositeExpression(); + } catch (ParseException pe) { + throw new ELException(pe.getMessage()); + } + } +} +PARSER_END( ELParser ) + +/* + * CompositeExpression + * Allow most flexible parsing, restrict by examining + * type of returned node + */ +AstCompositeExpression CompositeExpression() #CompositeExpression : {} +{ + (DeferredExpression() | DynamicExpression() | LiteralExpression())* { return jjtThis; } +} + +/* + * LiteralExpression + * Non-EL Expression blocks + */ +void LiteralExpression() #LiteralExpression : { Token t = null; } +{ + t= { jjtThis.setImage(t.image); } +} + +/* + * DeferredExpression + * #{..} Expressions + */ +void DeferredExpression() #DeferredExpression : {} +{ + Expression() +} + +/* + * DynamicExpression + * ${..} Expressions + */ +void DynamicExpression() #DynamicExpression : {} +{ + Expression() +} + +/* + * Expression + * EL Expression Language Root + */ +void Expression() : {} +{ + SemiColon() +} + +/* + * SemiColon + */ +void SemiColon() : {} +{ + Assignment() ( Assignment() #SemiColon(2) )* +} + +/* + * Assignment + * For '=', right associatve, then LambdaExpression or Choice or Assignment + */ +void Assignment() : {} +{ + LOOKAHEAD(3) LambdaExpression() | + Choice() ( Assignment() #Assign(2) )? +} + +/* + * LambdaExpression + */ +void LambdaExpression() #LambdaExpression : {} +{ + LambdaParameters() + (LOOKAHEAD(3) LambdaExpression() | Choice() ) +} + +void LambdaParameters() #LambdaParameters: {} +{ + Identifier() + | + (Identifier() ( Identifier())*)? + +} + +/* + * Choice + * For Choice markup a ? b : c, right associative + */ +void Choice() : {} +{ + Or() ( Choice() Choice() #Choice(3))? +} + +/* + * Or + * For 'or' '||', then And + */ +void Or() : {} +{ + And() ((|) And() #Or(2))* +} + +/* + * And + * For 'and' '&&', then Equality + */ +void And() : {} +{ + Equality() ((|) Equality() #And(2))* +} + +/* + * Equality + * For '==' 'eq' '!=' 'ne', then Compare + */ +void Equality() : {} +{ + Compare() + ( + ((|) Compare() #Equal(2)) + | + ((|) Compare() #NotEqual(2)) + )* +} + +/* + * Compare + * For a bunch of them, then Math + */ +void Compare() : {} +{ + Concatenation() + ( + ((|) Concatenation() #LessThan(2)) + | + ((|) Concatenation() #GreaterThan(2)) + | + ((|) Concatenation() #LessThanEqual(2)) + | + ((|) Concatenation() #GreaterThanEqual(2)) + )* +} + +/* + * Concatenation + * For '&', then Math() + */ +void Concatenation() : {} +{ + Math() ( Math() #Concat(2) )* +} + +/* + * Math + * For '+' '-', then Multiplication + */ +void Math() : {} +{ + Multiplication() + ( + ( Multiplication() #Plus(2)) + | + ( Multiplication() #Minus(2)) + )* +} + +/* + * Multiplication + * For a bunch of them, then Unary + */ +void Multiplication() : {} +{ + Unary() + ( + ( Unary() #Mult(2)) + | + ((|) Unary() #Div(2)) + | + ((|) Unary() #Mod(2)) + )* +} + +/* + * Unary + * For '-' '!' 'not' 'empty', then Value + */ +void Unary() : {} +{ + Unary() #Negative + | + (|) Unary() #Not + | + Unary() #Empty + | + Value() +} + +/* + * Value + * Defines Prefix plus zero or more Suffixes + */ +void Value() : {} +{ + (ValuePrefix() (ValueSuffix())*) #Value(>1) +} + +/* + * ValuePrefix + * For Literals, Variables, and Functions + */ +void ValuePrefix() : {} +{ + Literal() + | NonLiteral() +} + +/* + * ValueSuffix + * Either dot or bracket notation + */ +void ValueSuffix() : {} +{ + DotSuffix() | BracketSuffix() +} + +/* + * DotSuffix + * Dot Property and Dot Method + */ +void DotSuffix() #DotSuffix : { Token t = null; } +{ + t= { jjtThis.setImage(t.image); } + (MethodArguments())? +} + +/* + * BracketSuffix + * Sub Expression Suffix + */ +void BracketSuffix() #BracketSuffix : {} +{ + Expression() + (MethodArguments())? +} + + +/* + * MethodArguments + */ +void MethodArguments() #MethodArguments : {} +{ + (Expression() ( Expression())*)? +} + +/* + * Parenthesized Lambda Expression, with optional invokation + */ +void LambdaExpressionOrCall() #LambdaExpression : {} + +{ + + LambdaParameters() + (LOOKAHEAD(3) LambdaExpression() | Choice() ) + + (MethodArguments())* +} + +/* + * NonLiteral + * For Grouped Operations, Identifiers, and Functions + */ +void NonLiteral() : {} +{ + LOOKAHEAD(4) LambdaExpressionOrCall() + | Expression() + | LOOKAHEAD(4) Function() + | Identifier() + | MapData() + | ListData() +} + +void MapData() #MapData: {} +{ + + ( MapEntry() ( MapEntry() )* )? + +} + +void MapEntry() #MapEntry: {} +{ + Expression() ( Expression())? +} + +void ListData() #ListData: {} +{ + + ( Expression() ( Expression() )* )? + +} + +/* + * Identifier + * Java Language Identifier + */ +void Identifier() #Identifier : { Token t = null; } +{ + t= { jjtThis.setImage(t.image); } +} + +/* + * Function + * Namespace:Name(a,b,c) + */ +void Function() #Function : +{ + Token t0 = null; + Token t1 = null; +} +{ + t0= ( t1=)? + { + if (t1 != null) { + jjtThis.setPrefix(t0.image); + jjtThis.setLocalName(t1.image); + } else { + jjtThis.setLocalName(t0.image); + } + } + (MethodArguments())+ +} + + +/* + * Literal + * Reserved Keywords + */ +void Literal() : {} +{ + Boolean() + | FloatingPoint() + | Integer() + | String() + | Null() +} + +/* + * Boolean + * For 'true' 'false' + */ +void Boolean() : {} +{ + #True + | #False +} + +/* + * FloatinPoint + * For Decimal and Floating Point Literals + */ +void FloatingPoint() #FloatingPoint : { Token t = null; } +{ + t= { jjtThis.setImage(t.image); } +} + +/* + * Integer + * For Simple Numeric Literals + */ +void Integer() #Integer : { Token t = null; } +{ + t= { jjtThis.setImage(t.image); } +} + +/* + * String + * For Quoted Literals + */ +void String() #String : { Token t = null; } +{ + t= { jjtThis.setImage(t.image); } +} + +/* + * Null + * For 'null' + */ +void Null() #Null : {} +{ + +} + + +/* ========================================================================== */ +TOKEN_MGR_DECLS: +{ + java.util.Stack stack = new java.util.Stack(); +} + + TOKEN : +{ + < LITERAL_EXPRESSION: + ((~["\\", "$", "#"]) + | ("\\" ("\\" | "$" | "#")) + | ("$" ~["{", "$", "#"]) + | ("#" ~["{", "$", "#"]) + )+ + | "$" + | "#" + > +| + < START_DYNAMIC_EXPRESSION: "${" > {stack.push(DEFAULT);}: IN_EXPRESSION +| + < START_DEFERRED_EXPRESSION: "#{" > {stack.push(DEFAULT);}: IN_EXPRESSION +} + + SKIP : { "\\" } + + SKIP: +{ " " | "\t" | "\n" | "\r" } + + TOKEN : +{ + < START_MAP : "{" > {stack.push(curLexState);}: IN_MAP +| < RCURL: "}" > {SwitchTo(stack.pop());} +| < INTEGER_LITERAL: ["0"-"9"] (["0"-"9"])* > +| < FLOATING_POINT_LITERAL: (["0"-"9"])+ "." (["0"-"9"])* ()? + | "." (["0"-"9"])+ ()? + | (["0"-"9"])+ + > +| < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > +| < STRING_LITERAL: ("\"" ((~["\"","\\"]) + | ("\\" ( ["\\","\""] )))* "\"") + | ("\'" ((~["\'","\\"]) + | ("\\" ( ["\\","\'"] )))* "\'") + > +| < BADLY_ESCAPED_STRING_LITERAL: ("\"" (~["\"","\\"])* ("\\" ( ~["\\","\""] ))) + | ("\'" (~["\'","\\"])* ("\\" ( ~["\\","\'"] ))) + > +| < TRUE : "true" > +| < FALSE : "false" > +| < NULL : "null" > +| < DOT : "." > +| < LPAREN : "(" > +| < RPAREN : ")" > +| < LBRACK : "[" > +| < RBRACK : "]" > +| < COLON : ":" > +| < COMMA : "," > +| < SEMICOLON : ";" > +| < GT0 : ">" > +| < GT1 : "gt" > +| < LT0 : "<" > +| < LT1 : "lt" > +| < GE0 : ">=" > +| < GE1 : "ge" > +| < LE0 : "<=" > +| < LE1 : "le" > +| < EQ0 : "==" > +| < EQ1 : "eq" > +| < NE0 : "!=" > +| < NE1 : "ne" > +| < NOT0 : "!" > +| < NOT1 : "not" > +| < AND0 : "&&" > +| < AND1 : "and" > +| < OR0 : "||" > +| < OR1 : "or" > +| < EMPTY : "empty" > +| < INSTANCEOF : "instanceof" > +| < MULT : "*" > +| < PLUS : "+" > +| < MINUS : "-" > +| < QUESTIONMARK : "?" > +| < DIV0 : "/" > +| < DIV1 : "div" > +| < MOD0 : "%" > +| < MOD1 : "mod" > +| < CONCAT : "+=" > +| < ASSIGN : "=" > +| < ARROW : "->" > +| < IDENTIFIER : (|) (|)* > +| < #IMPL_OBJ_START: "#" > +| < #LETTER: + [ + "\u0024", + "\u0041"-"\u005a", + "\u005f", + "\u0061"-"\u007a", + "\u00c0"-"\u00d6", + "\u00d8"-"\u00f6", + "\u00f8"-"\u00ff", + "\u0100"-"\u1fff", + "\u3040"-"\u318f", + "\u3300"-"\u337f", + "\u3400"-"\u3d2d", + "\u4e00"-"\u9fff", + "\uf900"-"\ufaff" + ] + > +| < #DIGIT: + [ + "\u0030"-"\u0039", + "\u0660"-"\u0669", + "\u06f0"-"\u06f9", + "\u0966"-"\u096f", + "\u09e6"-"\u09ef", + "\u0a66"-"\u0a6f", + "\u0ae6"-"\u0aef", + "\u0b66"-"\u0b6f", + "\u0be7"-"\u0bef", + "\u0c66"-"\u0c6f", + "\u0ce6"-"\u0cef", + "\u0d66"-"\u0d6f", + "\u0e50"-"\u0e59", + "\u0ed0"-"\u0ed9", + "\u1040"-"\u1049" + ] + > +| < ILLEGAL_CHARACTER: (~[]) > +} + diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParserConstants.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParserConstants.java new file mode 100644 index 000000000..73943d9bc --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParserConstants.java @@ -0,0 +1,202 @@ +/* Generated By:JJTree&JavaCC: Do not edit this line. ELParserConstants.java */ +package com.sun.el.parser; + + +/** + * Token literal values and constants. + * Generated by org.javacc.parser.OtherFilesGen#start() + */ +public interface ELParserConstants { + + /** End of File. */ + int EOF = 0; + /** RegularExpression Id. */ + int LITERAL_EXPRESSION = 1; + /** RegularExpression Id. */ + int START_DYNAMIC_EXPRESSION = 2; + /** RegularExpression Id. */ + int START_DEFERRED_EXPRESSION = 3; + /** RegularExpression Id. */ + int START_MAP = 9; + /** RegularExpression Id. */ + int RCURL = 10; + /** RegularExpression Id. */ + int INTEGER_LITERAL = 11; + /** RegularExpression Id. */ + int FLOATING_POINT_LITERAL = 12; + /** RegularExpression Id. */ + int EXPONENT = 13; + /** RegularExpression Id. */ + int STRING_LITERAL = 14; + /** RegularExpression Id. */ + int BADLY_ESCAPED_STRING_LITERAL = 15; + /** RegularExpression Id. */ + int TRUE = 16; + /** RegularExpression Id. */ + int FALSE = 17; + /** RegularExpression Id. */ + int NULL = 18; + /** RegularExpression Id. */ + int DOT = 19; + /** RegularExpression Id. */ + int LPAREN = 20; + /** RegularExpression Id. */ + int RPAREN = 21; + /** RegularExpression Id. */ + int LBRACK = 22; + /** RegularExpression Id. */ + int RBRACK = 23; + /** RegularExpression Id. */ + int COLON = 24; + /** RegularExpression Id. */ + int COMMA = 25; + /** RegularExpression Id. */ + int SEMICOLON = 26; + /** RegularExpression Id. */ + int GT0 = 27; + /** RegularExpression Id. */ + int GT1 = 28; + /** RegularExpression Id. */ + int LT0 = 29; + /** RegularExpression Id. */ + int LT1 = 30; + /** RegularExpression Id. */ + int GE0 = 31; + /** RegularExpression Id. */ + int GE1 = 32; + /** RegularExpression Id. */ + int LE0 = 33; + /** RegularExpression Id. */ + int LE1 = 34; + /** RegularExpression Id. */ + int EQ0 = 35; + /** RegularExpression Id. */ + int EQ1 = 36; + /** RegularExpression Id. */ + int NE0 = 37; + /** RegularExpression Id. */ + int NE1 = 38; + /** RegularExpression Id. */ + int NOT0 = 39; + /** RegularExpression Id. */ + int NOT1 = 40; + /** RegularExpression Id. */ + int AND0 = 41; + /** RegularExpression Id. */ + int AND1 = 42; + /** RegularExpression Id. */ + int OR0 = 43; + /** RegularExpression Id. */ + int OR1 = 44; + /** RegularExpression Id. */ + int EMPTY = 45; + /** RegularExpression Id. */ + int INSTANCEOF = 46; + /** RegularExpression Id. */ + int MULT = 47; + /** RegularExpression Id. */ + int PLUS = 48; + /** RegularExpression Id. */ + int MINUS = 49; + /** RegularExpression Id. */ + int QUESTIONMARK = 50; + /** RegularExpression Id. */ + int DIV0 = 51; + /** RegularExpression Id. */ + int DIV1 = 52; + /** RegularExpression Id. */ + int MOD0 = 53; + /** RegularExpression Id. */ + int MOD1 = 54; + /** RegularExpression Id. */ + int CONCAT = 55; + /** RegularExpression Id. */ + int ASSIGN = 56; + /** RegularExpression Id. */ + int ARROW = 57; + /** RegularExpression Id. */ + int IDENTIFIER = 58; + /** RegularExpression Id. */ + int IMPL_OBJ_START = 59; + /** RegularExpression Id. */ + int LETTER = 60; + /** RegularExpression Id. */ + int DIGIT = 61; + /** RegularExpression Id. */ + int ILLEGAL_CHARACTER = 62; + + /** Lexical state. */ + int DEFAULT = 0; + /** Lexical state. */ + int IN_EXPRESSION = 1; + /** Lexical state. */ + int IN_MAP = 2; + + /** Literal token values. */ + String[] tokenImage = { + "", + "", + "\"${\"", + "\"#{\"", + "\"\\\\\"", + "\" \"", + "\"\\t\"", + "\"\\n\"", + "\"\\r\"", + "\"{\"", + "\"}\"", + "", + "", + "", + "", + "", + "\"true\"", + "\"false\"", + "\"null\"", + "\".\"", + "\"(\"", + "\")\"", + "\"[\"", + "\"]\"", + "\":\"", + "\",\"", + "\";\"", + "\">\"", + "\"gt\"", + "\"<\"", + "\"lt\"", + "\">=\"", + "\"ge\"", + "\"<=\"", + "\"le\"", + "\"==\"", + "\"eq\"", + "\"!=\"", + "\"ne\"", + "\"!\"", + "\"not\"", + "\"&&\"", + "\"and\"", + "\"||\"", + "\"or\"", + "\"empty\"", + "\"instanceof\"", + "\"*\"", + "\"+\"", + "\"-\"", + "\"?\"", + "\"/\"", + "\"div\"", + "\"%\"", + "\"mod\"", + "\"+=\"", + "\"=\"", + "\"->\"", + "", + "\"#\"", + "", + "", + "", + }; + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParserTokenManager.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParserTokenManager.java new file mode 100644 index 000000000..e41dd7b72 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParserTokenManager.java @@ -0,0 +1,2063 @@ +/* Generated By:JJTree&JavaCC: Do not edit this line. ELParserTokenManager.java */ +package com.sun.el.parser; +import java.io.StringReader; +import javax.el.ELException; + +/** Token Manager. */ +public class ELParserTokenManager implements ELParserConstants +{ + java.util.Stack stack = new java.util.Stack(); + + /** Debug output. */ + public java.io.PrintStream debugStream = System.out; + /** Set debug output. */ + public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; } +private final int jjStopStringLiteralDfa_0(int pos, long active0) +{ + switch (pos) + { + case 0: + if ((active0 & 0x10L) != 0L) + return 2; + if ((active0 & 0xcL) != 0L) + { + jjmatchedKind = 1; + return 4; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_0(int pos, long active0) +{ + return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1); +} +private int jjStopAtPos(int pos, int kind) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + return pos + 1; +} +private int jjMoveStringLiteralDfa0_0() +{ + switch(curChar) + { + case 35: + return jjMoveStringLiteralDfa1_0(0x8L); + case 36: + return jjMoveStringLiteralDfa1_0(0x4L); + case 92: + return jjStartNfaWithStates_0(0, 4, 2); + default : + return jjMoveNfa_0(6, 0); + } +} +private int jjMoveStringLiteralDfa1_0(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(0, active0); + return 1; + } + switch(curChar) + { + case 123: + if ((active0 & 0x4L) != 0L) + return jjStopAtPos(1, 2); + else if ((active0 & 0x8L) != 0L) + return jjStopAtPos(1, 3); + break; + default : + break; + } + return jjStartNfa_0(0, active0); +} +private int jjStartNfaWithStates_0(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_0(state, pos + 1); +} +static final long[] jjbitVec0 = { + 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec2 = { + 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +private int jjMoveNfa_0(int startState, int curPos) +{ + int startsAt = 0; + jjnewStateCnt = 7; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + do + { + switch(jjstateSet[--i]) + { + case 6: + if ((0xffffffe7ffffffffL & l) != 0L) + { + if (kind > 1) + kind = 1; + jjCheckNAddStates(0, 3); + } + else if ((0x1800000000L & l) != 0L) + { + if (kind > 1) + kind = 1; + } + if (curChar == 35) + jjCheckNAdd(4); + else if (curChar == 36) + jjCheckNAdd(4); + break; + case 0: + case 4: + if ((0xffffffe7ffffffffL & l) == 0L) + break; + if (kind > 1) + kind = 1; + jjCheckNAddStates(0, 3); + break; + case 2: + if ((0x1800000000L & l) == 0L) + break; + if (kind > 1) + kind = 1; + jjCheckNAddStates(0, 3); + break; + case 3: + if (curChar == 36) + jjCheckNAdd(4); + break; + case 5: + if (curChar == 35) + jjCheckNAdd(4); + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 6: + if ((0xffffffffefffffffL & l) != 0L) + { + if (kind > 1) + kind = 1; + jjCheckNAddStates(0, 3); + } + else if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 0: + if ((0xffffffffefffffffL & l) == 0L) + break; + if (kind > 1) + kind = 1; + jjCheckNAddStates(0, 3); + break; + case 1: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 2: + if (curChar != 92) + break; + if (kind > 1) + kind = 1; + jjCheckNAddStates(0, 3); + break; + case 4: + if ((0xf7ffffffffffffffL & l) == 0L) + break; + if (kind > 1) + kind = 1; + jjCheckNAddStates(0, 3); + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 6: + case 0: + case 4: + if (!jjCanMove_0(hiByte, i1, i2, l1, l2)) + break; + if (kind > 1) + kind = 1; + jjCheckNAddStates(0, 3); + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 7 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_2(int pos, long active0) +{ + switch (pos) + { + case 0: + if ((active0 & 0x80000L) != 0L) + return 1; + if ((active0 & 0x50755550070000L) != 0L) + { + jjmatchedKind = 58; + return 6; + } + return -1; + case 1: + if ((active0 & 0x105550000000L) != 0L) + return 6; + if ((active0 & 0x50650000070000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 1; + return 6; + } + return -1; + case 2: + if ((active0 & 0x50050000000000L) != 0L) + return 6; + if ((active0 & 0x600000070000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 2; + return 6; + } + return -1; + case 3: + if ((active0 & 0x50000L) != 0L) + return 6; + if ((active0 & 0x600000020000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 3; + return 6; + } + return -1; + case 4: + if ((active0 & 0x400000000000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 4; + return 6; + } + if ((active0 & 0x200000020000L) != 0L) + return 6; + return -1; + case 5: + if ((active0 & 0x400000000000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 5; + return 6; + } + return -1; + case 6: + if ((active0 & 0x400000000000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 6; + return 6; + } + return -1; + case 7: + if ((active0 & 0x400000000000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 7; + return 6; + } + return -1; + case 8: + if ((active0 & 0x400000000000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 8; + return 6; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_2(int pos, long active0) +{ + return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0), pos + 1); +} +private int jjMoveStringLiteralDfa0_2() +{ + switch(curChar) + { + case 33: + jjmatchedKind = 39; + return jjMoveStringLiteralDfa1_2(0x2000000000L); + case 37: + return jjStopAtPos(0, 53); + case 38: + return jjMoveStringLiteralDfa1_2(0x20000000000L); + case 40: + return jjStopAtPos(0, 20); + case 41: + return jjStopAtPos(0, 21); + case 42: + return jjStopAtPos(0, 47); + case 43: + jjmatchedKind = 48; + return jjMoveStringLiteralDfa1_2(0x80000000000000L); + case 44: + return jjStopAtPos(0, 25); + case 45: + jjmatchedKind = 49; + return jjMoveStringLiteralDfa1_2(0x200000000000000L); + case 46: + return jjStartNfaWithStates_2(0, 19, 1); + case 47: + return jjStopAtPos(0, 51); + case 58: + return jjStopAtPos(0, 24); + case 59: + return jjStopAtPos(0, 26); + case 60: + jjmatchedKind = 29; + return jjMoveStringLiteralDfa1_2(0x200000000L); + case 61: + jjmatchedKind = 56; + return jjMoveStringLiteralDfa1_2(0x800000000L); + case 62: + jjmatchedKind = 27; + return jjMoveStringLiteralDfa1_2(0x80000000L); + case 63: + return jjStopAtPos(0, 50); + case 91: + return jjStopAtPos(0, 22); + case 93: + return jjStopAtPos(0, 23); + case 97: + return jjMoveStringLiteralDfa1_2(0x40000000000L); + case 100: + return jjMoveStringLiteralDfa1_2(0x10000000000000L); + case 101: + return jjMoveStringLiteralDfa1_2(0x201000000000L); + case 102: + return jjMoveStringLiteralDfa1_2(0x20000L); + case 103: + return jjMoveStringLiteralDfa1_2(0x110000000L); + case 105: + return jjMoveStringLiteralDfa1_2(0x400000000000L); + case 108: + return jjMoveStringLiteralDfa1_2(0x440000000L); + case 109: + return jjMoveStringLiteralDfa1_2(0x40000000000000L); + case 110: + return jjMoveStringLiteralDfa1_2(0x14000040000L); + case 111: + return jjMoveStringLiteralDfa1_2(0x100000000000L); + case 116: + return jjMoveStringLiteralDfa1_2(0x10000L); + case 123: + return jjStopAtPos(0, 9); + case 124: + return jjMoveStringLiteralDfa1_2(0x80000000000L); + case 125: + return jjStopAtPos(0, 10); + default : + return jjMoveNfa_2(0, 0); + } +} +private int jjMoveStringLiteralDfa1_2(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(0, active0); + return 1; + } + switch(curChar) + { + case 38: + if ((active0 & 0x20000000000L) != 0L) + return jjStopAtPos(1, 41); + break; + case 61: + if ((active0 & 0x80000000L) != 0L) + return jjStopAtPos(1, 31); + else if ((active0 & 0x200000000L) != 0L) + return jjStopAtPos(1, 33); + else if ((active0 & 0x800000000L) != 0L) + return jjStopAtPos(1, 35); + else if ((active0 & 0x2000000000L) != 0L) + return jjStopAtPos(1, 37); + else if ((active0 & 0x80000000000000L) != 0L) + return jjStopAtPos(1, 55); + break; + case 62: + if ((active0 & 0x200000000000000L) != 0L) + return jjStopAtPos(1, 57); + break; + case 97: + return jjMoveStringLiteralDfa2_2(active0, 0x20000L); + case 101: + if ((active0 & 0x100000000L) != 0L) + return jjStartNfaWithStates_2(1, 32, 6); + else if ((active0 & 0x400000000L) != 0L) + return jjStartNfaWithStates_2(1, 34, 6); + else if ((active0 & 0x4000000000L) != 0L) + return jjStartNfaWithStates_2(1, 38, 6); + break; + case 105: + return jjMoveStringLiteralDfa2_2(active0, 0x10000000000000L); + case 109: + return jjMoveStringLiteralDfa2_2(active0, 0x200000000000L); + case 110: + return jjMoveStringLiteralDfa2_2(active0, 0x440000000000L); + case 111: + return jjMoveStringLiteralDfa2_2(active0, 0x40010000000000L); + case 113: + if ((active0 & 0x1000000000L) != 0L) + return jjStartNfaWithStates_2(1, 36, 6); + break; + case 114: + if ((active0 & 0x100000000000L) != 0L) + return jjStartNfaWithStates_2(1, 44, 6); + return jjMoveStringLiteralDfa2_2(active0, 0x10000L); + case 116: + if ((active0 & 0x10000000L) != 0L) + return jjStartNfaWithStates_2(1, 28, 6); + else if ((active0 & 0x40000000L) != 0L) + return jjStartNfaWithStates_2(1, 30, 6); + break; + case 117: + return jjMoveStringLiteralDfa2_2(active0, 0x40000L); + case 124: + if ((active0 & 0x80000000000L) != 0L) + return jjStopAtPos(1, 43); + break; + default : + break; + } + return jjStartNfa_2(0, active0); +} +private int jjMoveStringLiteralDfa2_2(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(0, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(1, active0); + return 2; + } + switch(curChar) + { + case 100: + if ((active0 & 0x40000000000L) != 0L) + return jjStartNfaWithStates_2(2, 42, 6); + else if ((active0 & 0x40000000000000L) != 0L) + return jjStartNfaWithStates_2(2, 54, 6); + break; + case 108: + return jjMoveStringLiteralDfa3_2(active0, 0x60000L); + case 112: + return jjMoveStringLiteralDfa3_2(active0, 0x200000000000L); + case 115: + return jjMoveStringLiteralDfa3_2(active0, 0x400000000000L); + case 116: + if ((active0 & 0x10000000000L) != 0L) + return jjStartNfaWithStates_2(2, 40, 6); + break; + case 117: + return jjMoveStringLiteralDfa3_2(active0, 0x10000L); + case 118: + if ((active0 & 0x10000000000000L) != 0L) + return jjStartNfaWithStates_2(2, 52, 6); + break; + default : + break; + } + return jjStartNfa_2(1, active0); +} +private int jjMoveStringLiteralDfa3_2(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(1, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(2, active0); + return 3; + } + switch(curChar) + { + case 101: + if ((active0 & 0x10000L) != 0L) + return jjStartNfaWithStates_2(3, 16, 6); + break; + case 108: + if ((active0 & 0x40000L) != 0L) + return jjStartNfaWithStates_2(3, 18, 6); + break; + case 115: + return jjMoveStringLiteralDfa4_2(active0, 0x20000L); + case 116: + return jjMoveStringLiteralDfa4_2(active0, 0x600000000000L); + default : + break; + } + return jjStartNfa_2(2, active0); +} +private int jjMoveStringLiteralDfa4_2(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(2, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(3, active0); + return 4; + } + switch(curChar) + { + case 97: + return jjMoveStringLiteralDfa5_2(active0, 0x400000000000L); + case 101: + if ((active0 & 0x20000L) != 0L) + return jjStartNfaWithStates_2(4, 17, 6); + break; + case 121: + if ((active0 & 0x200000000000L) != 0L) + return jjStartNfaWithStates_2(4, 45, 6); + break; + default : + break; + } + return jjStartNfa_2(3, active0); +} +private int jjMoveStringLiteralDfa5_2(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(3, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(4, active0); + return 5; + } + switch(curChar) + { + case 110: + return jjMoveStringLiteralDfa6_2(active0, 0x400000000000L); + default : + break; + } + return jjStartNfa_2(4, active0); +} +private int jjMoveStringLiteralDfa6_2(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(4, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(5, active0); + return 6; + } + switch(curChar) + { + case 99: + return jjMoveStringLiteralDfa7_2(active0, 0x400000000000L); + default : + break; + } + return jjStartNfa_2(5, active0); +} +private int jjMoveStringLiteralDfa7_2(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(5, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(6, active0); + return 7; + } + switch(curChar) + { + case 101: + return jjMoveStringLiteralDfa8_2(active0, 0x400000000000L); + default : + break; + } + return jjStartNfa_2(6, active0); +} +private int jjMoveStringLiteralDfa8_2(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(6, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(7, active0); + return 8; + } + switch(curChar) + { + case 111: + return jjMoveStringLiteralDfa9_2(active0, 0x400000000000L); + default : + break; + } + return jjStartNfa_2(7, active0); +} +private int jjMoveStringLiteralDfa9_2(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_2(7, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_2(8, active0); + return 9; + } + switch(curChar) + { + case 102: + if ((active0 & 0x400000000000L) != 0L) + return jjStartNfaWithStates_2(9, 46, 6); + break; + default : + break; + } + return jjStartNfa_2(8, active0); +} +private int jjStartNfaWithStates_2(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_2(state, pos + 1); +} +static final long[] jjbitVec3 = { + 0x1ff00000fffffffeL, 0xffffffffffffc000L, 0xffffffffL, 0x600000000000000L +}; +static final long[] jjbitVec4 = { + 0x0L, 0x0L, 0x0L, 0xff7fffffff7fffffL +}; +static final long[] jjbitVec5 = { + 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec6 = { + 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffL, 0x0L +}; +static final long[] jjbitVec7 = { + 0xffffffffffffffffL, 0xffffffffffffffffL, 0x0L, 0x0L +}; +static final long[] jjbitVec8 = { + 0x3fffffffffffL, 0x0L, 0x0L, 0x0L +}; +private int jjMoveNfa_2(int startState, int curPos) +{ + int startsAt = 0; + jjnewStateCnt = 35; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x3ff000000000000L & l) != 0L) + { + if (kind > 11) + kind = 11; + jjCheckNAddStates(4, 8); + } + else if ((0x1800000000L & l) != 0L) + { + if (kind > 58) + kind = 58; + jjCheckNAdd(6); + } + else if (curChar == 39) + jjCheckNAddStates(9, 13); + else if (curChar == 34) + jjCheckNAddStates(14, 18); + else if (curChar == 46) + jjCheckNAdd(1); + break; + case 1: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAddTwoStates(1, 2); + break; + case 3: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(4); + break; + case 4: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAdd(4); + break; + case 5: + if ((0x1800000000L & l) == 0L) + break; + if (kind > 58) + kind = 58; + jjCheckNAdd(6); + break; + case 6: + if ((0x3ff001000000000L & l) == 0L) + break; + if (kind > 58) + kind = 58; + jjCheckNAdd(6); + break; + case 7: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 11) + kind = 11; + jjCheckNAddStates(4, 8); + break; + case 8: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 11) + kind = 11; + jjCheckNAdd(8); + break; + case 9: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(9, 10); + break; + case 10: + if (curChar != 46) + break; + if (kind > 12) + kind = 12; + jjCheckNAddTwoStates(11, 12); + break; + case 11: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAddTwoStates(11, 12); + break; + case 13: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(14); + break; + case 14: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAdd(14); + break; + case 15: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(15, 16); + break; + case 17: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(18); + break; + case 18: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAdd(18); + break; + case 19: + if (curChar == 34) + jjCheckNAddStates(14, 18); + break; + case 20: + if ((0xfffffffbffffffffL & l) != 0L) + jjCheckNAddStates(19, 21); + break; + case 22: + if (curChar == 34) + jjCheckNAddStates(19, 21); + break; + case 23: + if (curChar == 34 && kind > 14) + kind = 14; + break; + case 24: + if ((0xfffffffbffffffffL & l) != 0L) + jjCheckNAddTwoStates(24, 25); + break; + case 26: + if ((0xfffffffbffffffffL & l) != 0L && kind > 15) + kind = 15; + break; + case 27: + if (curChar == 39) + jjCheckNAddStates(9, 13); + break; + case 28: + if ((0xffffff7fffffffffL & l) != 0L) + jjCheckNAddStates(22, 24); + break; + case 30: + if (curChar == 39) + jjCheckNAddStates(22, 24); + break; + case 31: + if (curChar == 39 && kind > 14) + kind = 14; + break; + case 32: + if ((0xffffff7fffffffffL & l) != 0L) + jjCheckNAddTwoStates(32, 33); + break; + case 34: + if ((0xffffff7fffffffffL & l) != 0L && kind > 15) + kind = 15; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 0: + case 6: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 58) + kind = 58; + jjCheckNAdd(6); + break; + case 2: + if ((0x2000000020L & l) != 0L) + jjAddStates(25, 26); + break; + case 12: + if ((0x2000000020L & l) != 0L) + jjAddStates(27, 28); + break; + case 16: + if ((0x2000000020L & l) != 0L) + jjAddStates(29, 30); + break; + case 20: + if ((0xffffffffefffffffL & l) != 0L) + jjCheckNAddStates(19, 21); + break; + case 21: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 22; + break; + case 22: + if (curChar == 92) + jjCheckNAddStates(19, 21); + break; + case 24: + if ((0xffffffffefffffffL & l) != 0L) + jjAddStates(31, 32); + break; + case 25: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 26; + break; + case 26: + case 34: + if ((0xffffffffefffffffL & l) != 0L && kind > 15) + kind = 15; + break; + case 28: + if ((0xffffffffefffffffL & l) != 0L) + jjCheckNAddStates(22, 24); + break; + case 29: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 30; + break; + case 30: + if (curChar == 92) + jjCheckNAddStates(22, 24); + break; + case 32: + if ((0xffffffffefffffffL & l) != 0L) + jjAddStates(33, 34); + break; + case 33: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 34; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 0: + case 6: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 58) + kind = 58; + jjCheckNAdd(6); + break; + case 20: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(19, 21); + break; + case 24: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(31, 32); + break; + case 26: + case 34: + if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 15) + kind = 15; + break; + case 28: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(22, 24); + break; + case 32: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(33, 34); + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 35 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private final int jjStopStringLiteralDfa_1(int pos, long active0) +{ + switch (pos) + { + case 0: + if ((active0 & 0x80000L) != 0L) + return 1; + if ((active0 & 0x50755550070000L) != 0L) + { + jjmatchedKind = 58; + return 6; + } + return -1; + case 1: + if ((active0 & 0x105550000000L) != 0L) + return 6; + if ((active0 & 0x50650000070000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 1; + return 6; + } + return -1; + case 2: + if ((active0 & 0x50050000000000L) != 0L) + return 6; + if ((active0 & 0x600000070000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 2; + return 6; + } + return -1; + case 3: + if ((active0 & 0x50000L) != 0L) + return 6; + if ((active0 & 0x600000020000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 3; + return 6; + } + return -1; + case 4: + if ((active0 & 0x400000000000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 4; + return 6; + } + if ((active0 & 0x200000020000L) != 0L) + return 6; + return -1; + case 5: + if ((active0 & 0x400000000000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 5; + return 6; + } + return -1; + case 6: + if ((active0 & 0x400000000000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 6; + return 6; + } + return -1; + case 7: + if ((active0 & 0x400000000000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 7; + return 6; + } + return -1; + case 8: + if ((active0 & 0x400000000000L) != 0L) + { + jjmatchedKind = 58; + jjmatchedPos = 8; + return 6; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_1(int pos, long active0) +{ + return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0), pos + 1); +} +private int jjMoveStringLiteralDfa0_1() +{ + switch(curChar) + { + case 33: + jjmatchedKind = 39; + return jjMoveStringLiteralDfa1_1(0x2000000000L); + case 37: + return jjStopAtPos(0, 53); + case 38: + return jjMoveStringLiteralDfa1_1(0x20000000000L); + case 40: + return jjStopAtPos(0, 20); + case 41: + return jjStopAtPos(0, 21); + case 42: + return jjStopAtPos(0, 47); + case 43: + jjmatchedKind = 48; + return jjMoveStringLiteralDfa1_1(0x80000000000000L); + case 44: + return jjStopAtPos(0, 25); + case 45: + jjmatchedKind = 49; + return jjMoveStringLiteralDfa1_1(0x200000000000000L); + case 46: + return jjStartNfaWithStates_1(0, 19, 1); + case 47: + return jjStopAtPos(0, 51); + case 58: + return jjStopAtPos(0, 24); + case 59: + return jjStopAtPos(0, 26); + case 60: + jjmatchedKind = 29; + return jjMoveStringLiteralDfa1_1(0x200000000L); + case 61: + jjmatchedKind = 56; + return jjMoveStringLiteralDfa1_1(0x800000000L); + case 62: + jjmatchedKind = 27; + return jjMoveStringLiteralDfa1_1(0x80000000L); + case 63: + return jjStopAtPos(0, 50); + case 91: + return jjStopAtPos(0, 22); + case 93: + return jjStopAtPos(0, 23); + case 97: + return jjMoveStringLiteralDfa1_1(0x40000000000L); + case 100: + return jjMoveStringLiteralDfa1_1(0x10000000000000L); + case 101: + return jjMoveStringLiteralDfa1_1(0x201000000000L); + case 102: + return jjMoveStringLiteralDfa1_1(0x20000L); + case 103: + return jjMoveStringLiteralDfa1_1(0x110000000L); + case 105: + return jjMoveStringLiteralDfa1_1(0x400000000000L); + case 108: + return jjMoveStringLiteralDfa1_1(0x440000000L); + case 109: + return jjMoveStringLiteralDfa1_1(0x40000000000000L); + case 110: + return jjMoveStringLiteralDfa1_1(0x14000040000L); + case 111: + return jjMoveStringLiteralDfa1_1(0x100000000000L); + case 116: + return jjMoveStringLiteralDfa1_1(0x10000L); + case 123: + return jjStopAtPos(0, 9); + case 124: + return jjMoveStringLiteralDfa1_1(0x80000000000L); + case 125: + return jjStopAtPos(0, 10); + default : + return jjMoveNfa_1(0, 0); + } +} +private int jjMoveStringLiteralDfa1_1(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(0, active0); + return 1; + } + switch(curChar) + { + case 38: + if ((active0 & 0x20000000000L) != 0L) + return jjStopAtPos(1, 41); + break; + case 61: + if ((active0 & 0x80000000L) != 0L) + return jjStopAtPos(1, 31); + else if ((active0 & 0x200000000L) != 0L) + return jjStopAtPos(1, 33); + else if ((active0 & 0x800000000L) != 0L) + return jjStopAtPos(1, 35); + else if ((active0 & 0x2000000000L) != 0L) + return jjStopAtPos(1, 37); + else if ((active0 & 0x80000000000000L) != 0L) + return jjStopAtPos(1, 55); + break; + case 62: + if ((active0 & 0x200000000000000L) != 0L) + return jjStopAtPos(1, 57); + break; + case 97: + return jjMoveStringLiteralDfa2_1(active0, 0x20000L); + case 101: + if ((active0 & 0x100000000L) != 0L) + return jjStartNfaWithStates_1(1, 32, 6); + else if ((active0 & 0x400000000L) != 0L) + return jjStartNfaWithStates_1(1, 34, 6); + else if ((active0 & 0x4000000000L) != 0L) + return jjStartNfaWithStates_1(1, 38, 6); + break; + case 105: + return jjMoveStringLiteralDfa2_1(active0, 0x10000000000000L); + case 109: + return jjMoveStringLiteralDfa2_1(active0, 0x200000000000L); + case 110: + return jjMoveStringLiteralDfa2_1(active0, 0x440000000000L); + case 111: + return jjMoveStringLiteralDfa2_1(active0, 0x40010000000000L); + case 113: + if ((active0 & 0x1000000000L) != 0L) + return jjStartNfaWithStates_1(1, 36, 6); + break; + case 114: + if ((active0 & 0x100000000000L) != 0L) + return jjStartNfaWithStates_1(1, 44, 6); + return jjMoveStringLiteralDfa2_1(active0, 0x10000L); + case 116: + if ((active0 & 0x10000000L) != 0L) + return jjStartNfaWithStates_1(1, 28, 6); + else if ((active0 & 0x40000000L) != 0L) + return jjStartNfaWithStates_1(1, 30, 6); + break; + case 117: + return jjMoveStringLiteralDfa2_1(active0, 0x40000L); + case 124: + if ((active0 & 0x80000000000L) != 0L) + return jjStopAtPos(1, 43); + break; + default : + break; + } + return jjStartNfa_1(0, active0); +} +private int jjMoveStringLiteralDfa2_1(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(0, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(1, active0); + return 2; + } + switch(curChar) + { + case 100: + if ((active0 & 0x40000000000L) != 0L) + return jjStartNfaWithStates_1(2, 42, 6); + else if ((active0 & 0x40000000000000L) != 0L) + return jjStartNfaWithStates_1(2, 54, 6); + break; + case 108: + return jjMoveStringLiteralDfa3_1(active0, 0x60000L); + case 112: + return jjMoveStringLiteralDfa3_1(active0, 0x200000000000L); + case 115: + return jjMoveStringLiteralDfa3_1(active0, 0x400000000000L); + case 116: + if ((active0 & 0x10000000000L) != 0L) + return jjStartNfaWithStates_1(2, 40, 6); + break; + case 117: + return jjMoveStringLiteralDfa3_1(active0, 0x10000L); + case 118: + if ((active0 & 0x10000000000000L) != 0L) + return jjStartNfaWithStates_1(2, 52, 6); + break; + default : + break; + } + return jjStartNfa_1(1, active0); +} +private int jjMoveStringLiteralDfa3_1(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(1, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(2, active0); + return 3; + } + switch(curChar) + { + case 101: + if ((active0 & 0x10000L) != 0L) + return jjStartNfaWithStates_1(3, 16, 6); + break; + case 108: + if ((active0 & 0x40000L) != 0L) + return jjStartNfaWithStates_1(3, 18, 6); + break; + case 115: + return jjMoveStringLiteralDfa4_1(active0, 0x20000L); + case 116: + return jjMoveStringLiteralDfa4_1(active0, 0x600000000000L); + default : + break; + } + return jjStartNfa_1(2, active0); +} +private int jjMoveStringLiteralDfa4_1(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(2, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(3, active0); + return 4; + } + switch(curChar) + { + case 97: + return jjMoveStringLiteralDfa5_1(active0, 0x400000000000L); + case 101: + if ((active0 & 0x20000L) != 0L) + return jjStartNfaWithStates_1(4, 17, 6); + break; + case 121: + if ((active0 & 0x200000000000L) != 0L) + return jjStartNfaWithStates_1(4, 45, 6); + break; + default : + break; + } + return jjStartNfa_1(3, active0); +} +private int jjMoveStringLiteralDfa5_1(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(3, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(4, active0); + return 5; + } + switch(curChar) + { + case 110: + return jjMoveStringLiteralDfa6_1(active0, 0x400000000000L); + default : + break; + } + return jjStartNfa_1(4, active0); +} +private int jjMoveStringLiteralDfa6_1(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(4, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(5, active0); + return 6; + } + switch(curChar) + { + case 99: + return jjMoveStringLiteralDfa7_1(active0, 0x400000000000L); + default : + break; + } + return jjStartNfa_1(5, active0); +} +private int jjMoveStringLiteralDfa7_1(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(5, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(6, active0); + return 7; + } + switch(curChar) + { + case 101: + return jjMoveStringLiteralDfa8_1(active0, 0x400000000000L); + default : + break; + } + return jjStartNfa_1(6, active0); +} +private int jjMoveStringLiteralDfa8_1(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(6, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(7, active0); + return 8; + } + switch(curChar) + { + case 111: + return jjMoveStringLiteralDfa9_1(active0, 0x400000000000L); + default : + break; + } + return jjStartNfa_1(7, active0); +} +private int jjMoveStringLiteralDfa9_1(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_1(7, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_1(8, active0); + return 9; + } + switch(curChar) + { + case 102: + if ((active0 & 0x400000000000L) != 0L) + return jjStartNfaWithStates_1(9, 46, 6); + break; + default : + break; + } + return jjStartNfa_1(8, active0); +} +private int jjStartNfaWithStates_1(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_1(state, pos + 1); +} +private int jjMoveNfa_1(int startState, int curPos) +{ + int startsAt = 0; + jjnewStateCnt = 35; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0x3ff000000000000L & l) != 0L) + { + if (kind > 11) + kind = 11; + jjCheckNAddStates(4, 8); + } + else if ((0x1800000000L & l) != 0L) + { + if (kind > 58) + kind = 58; + jjCheckNAdd(6); + } + else if (curChar == 39) + jjCheckNAddStates(9, 13); + else if (curChar == 34) + jjCheckNAddStates(14, 18); + else if (curChar == 46) + jjCheckNAdd(1); + break; + case 1: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAddTwoStates(1, 2); + break; + case 3: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(4); + break; + case 4: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAdd(4); + break; + case 5: + if ((0x1800000000L & l) == 0L) + break; + if (kind > 58) + kind = 58; + jjCheckNAdd(6); + break; + case 6: + if ((0x3ff001000000000L & l) == 0L) + break; + if (kind > 58) + kind = 58; + jjCheckNAdd(6); + break; + case 7: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 11) + kind = 11; + jjCheckNAddStates(4, 8); + break; + case 8: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 11) + kind = 11; + jjCheckNAdd(8); + break; + case 9: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(9, 10); + break; + case 10: + if (curChar != 46) + break; + if (kind > 12) + kind = 12; + jjCheckNAddTwoStates(11, 12); + break; + case 11: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAddTwoStates(11, 12); + break; + case 13: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(14); + break; + case 14: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAdd(14); + break; + case 15: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(15, 16); + break; + case 17: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(18); + break; + case 18: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 12) + kind = 12; + jjCheckNAdd(18); + break; + case 19: + if (curChar == 34) + jjCheckNAddStates(14, 18); + break; + case 20: + if ((0xfffffffbffffffffL & l) != 0L) + jjCheckNAddStates(19, 21); + break; + case 22: + if (curChar == 34) + jjCheckNAddStates(19, 21); + break; + case 23: + if (curChar == 34 && kind > 14) + kind = 14; + break; + case 24: + if ((0xfffffffbffffffffL & l) != 0L) + jjCheckNAddTwoStates(24, 25); + break; + case 26: + if ((0xfffffffbffffffffL & l) != 0L && kind > 15) + kind = 15; + break; + case 27: + if (curChar == 39) + jjCheckNAddStates(9, 13); + break; + case 28: + if ((0xffffff7fffffffffL & l) != 0L) + jjCheckNAddStates(22, 24); + break; + case 30: + if (curChar == 39) + jjCheckNAddStates(22, 24); + break; + case 31: + if (curChar == 39 && kind > 14) + kind = 14; + break; + case 32: + if ((0xffffff7fffffffffL & l) != 0L) + jjCheckNAddTwoStates(32, 33); + break; + case 34: + if ((0xffffff7fffffffffL & l) != 0L && kind > 15) + kind = 15; + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 0: + case 6: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 58) + kind = 58; + jjCheckNAdd(6); + break; + case 2: + if ((0x2000000020L & l) != 0L) + jjAddStates(25, 26); + break; + case 12: + if ((0x2000000020L & l) != 0L) + jjAddStates(27, 28); + break; + case 16: + if ((0x2000000020L & l) != 0L) + jjAddStates(29, 30); + break; + case 20: + if ((0xffffffffefffffffL & l) != 0L) + jjCheckNAddStates(19, 21); + break; + case 21: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 22; + break; + case 22: + if (curChar == 92) + jjCheckNAddStates(19, 21); + break; + case 24: + if ((0xffffffffefffffffL & l) != 0L) + jjAddStates(31, 32); + break; + case 25: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 26; + break; + case 26: + case 34: + if ((0xffffffffefffffffL & l) != 0L && kind > 15) + kind = 15; + break; + case 28: + if ((0xffffffffefffffffL & l) != 0L) + jjCheckNAddStates(22, 24); + break; + case 29: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 30; + break; + case 30: + if (curChar == 92) + jjCheckNAddStates(22, 24); + break; + case 32: + if ((0xffffffffefffffffL & l) != 0L) + jjAddStates(33, 34); + break; + case 33: + if (curChar == 92) + jjstateSet[jjnewStateCnt++] = 34; + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 0: + case 6: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) + break; + if (kind > 58) + kind = 58; + jjCheckNAdd(6); + break; + case 20: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(19, 21); + break; + case 24: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(31, 32); + break; + case 26: + case 34: + if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 15) + kind = 15; + break; + case 28: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(22, 24); + break; + case 32: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(33, 34); + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 35 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +static final int[] jjnextStates = { + 0, 1, 3, 5, 8, 9, 10, 15, 16, 28, 29, 31, 32, 33, 20, 21, + 23, 24, 25, 20, 21, 23, 28, 29, 31, 3, 4, 13, 14, 17, 18, 24, + 25, 32, 33, +}; +private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec2[i2] & l2) != 0L); + default : + if ((jjbitVec0[i1] & l1) != 0L) + return true; + return false; + } +} +private static final boolean jjCanMove_1(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec4[i2] & l2) != 0L); + case 48: + return ((jjbitVec5[i2] & l2) != 0L); + case 49: + return ((jjbitVec6[i2] & l2) != 0L); + case 51: + return ((jjbitVec7[i2] & l2) != 0L); + case 61: + return ((jjbitVec8[i2] & l2) != 0L); + default : + if ((jjbitVec3[i1] & l1) != 0L) + return true; + return false; + } +} + +/** Token literal values. */ +public static final String[] jjstrLiteralImages = { +"", null, "\44\173", "\43\173", null, null, null, null, null, "\173", "\175", +null, null, null, null, null, "\164\162\165\145", "\146\141\154\163\145", +"\156\165\154\154", "\56", "\50", "\51", "\133", "\135", "\72", "\54", "\73", "\76", "\147\164", +"\74", "\154\164", "\76\75", "\147\145", "\74\75", "\154\145", "\75\75", "\145\161", +"\41\75", "\156\145", "\41", "\156\157\164", "\46\46", "\141\156\144", "\174\174", +"\157\162", "\145\155\160\164\171", "\151\156\163\164\141\156\143\145\157\146", "\52", +"\53", "\55", "\77", "\57", "\144\151\166", "\45", "\155\157\144", "\53\75", "\75", +"\55\76", null, null, null, null, null, }; + +/** Lexer state names. */ +public static final String[] lexStateNames = { + "DEFAULT", + "IN_EXPRESSION", + "IN_MAP", +}; + +/** Lex State array. */ +public static final int[] jjnewLexState = { + -1, -1, 1, 1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; +static final long[] jjtoToken = { + 0x47ffffffffffde0fL, +}; +static final long[] jjtoSkip = { + 0x1f0L, +}; +protected SimpleCharStream input_stream; +private final int[] jjrounds = new int[35]; +private final int[] jjstateSet = new int[70]; +private final StringBuilder jjimage = new StringBuilder(); +private StringBuilder image = jjimage; +private int jjimageLen; +private int lengthOfMatch; +protected char curChar; +/** Constructor. */ +public ELParserTokenManager(SimpleCharStream stream){ + if (SimpleCharStream.staticFlag) + throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer."); + input_stream = stream; +} + +/** Constructor. */ +public ELParserTokenManager(SimpleCharStream stream, int lexState){ + this(stream); + SwitchTo(lexState); +} + +/** Reinitialise parser. */ +public void ReInit(SimpleCharStream stream) +{ + jjmatchedPos = jjnewStateCnt = 0; + curLexState = defaultLexState; + input_stream = stream; + ReInitRounds(); +} +private void ReInitRounds() +{ + int i; + jjround = 0x80000001; + for (i = 35; i-- > 0;) + jjrounds[i] = 0x80000000; +} + +/** Reinitialise parser. */ +public void ReInit(SimpleCharStream stream, int lexState) +{ + ReInit(stream); + SwitchTo(lexState); +} + +/** Switch to specified lex state. */ +public void SwitchTo(int lexState) +{ + if (lexState >= 3 || lexState < 0) + throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE); + else + curLexState = lexState; +} + +protected Token jjFillToken() +{ + final Token t; + final String curTokenImage; + final int beginLine; + final int endLine; + final int beginColumn; + final int endColumn; + String im = jjstrLiteralImages[jjmatchedKind]; + curTokenImage = (im == null) ? input_stream.GetImage() : im; + beginLine = input_stream.getBeginLine(); + beginColumn = input_stream.getBeginColumn(); + endLine = input_stream.getEndLine(); + endColumn = input_stream.getEndColumn(); + t = Token.newToken(jjmatchedKind); + t.kind = jjmatchedKind; + t.image = curTokenImage; + + t.beginLine = beginLine; + t.endLine = endLine; + t.beginColumn = beginColumn; + t.endColumn = endColumn; + + return t; +} + +int curLexState = 0; +int defaultLexState = 0; +int jjnewStateCnt; +int jjround; +int jjmatchedPos; +int jjmatchedKind; + +/** Get the next Token. */ +public Token getNextToken() +{ + Token matchedToken; + int curPos = 0; + + EOFLoop : + for (;;) + { + try + { + curChar = input_stream.BeginToken(); + } + catch(java.io.IOException e) + { + jjmatchedKind = 0; + matchedToken = jjFillToken(); + return matchedToken; + } + image = jjimage; + image.setLength(0); + jjimageLen = 0; + + switch(curLexState) + { + case 0: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_0(); + break; + case 1: + try { input_stream.backup(0); + while (curChar <= 32 && (0x100002600L & (1L << curChar)) != 0L) + curChar = input_stream.BeginToken(); + } + catch (java.io.IOException e1) { continue EOFLoop; } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_1(); + if (jjmatchedPos == 0 && jjmatchedKind > 62) + { + jjmatchedKind = 62; + } + break; + case 2: + try { input_stream.backup(0); + while (curChar <= 32 && (0x100002600L & (1L << curChar)) != 0L) + curChar = input_stream.BeginToken(); + } + catch (java.io.IOException e1) { continue EOFLoop; } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_2(); + if (jjmatchedPos == 0 && jjmatchedKind > 62) + { + jjmatchedKind = 62; + } + break; + } + if (jjmatchedKind != 0x7fffffff) + { + if (jjmatchedPos + 1 < curPos) + input_stream.backup(curPos - jjmatchedPos - 1); + if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) + { + matchedToken = jjFillToken(); + TokenLexicalActions(matchedToken); + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + return matchedToken; + } + else + { + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + continue EOFLoop; + } + } + int error_line = input_stream.getEndLine(); + int error_column = input_stream.getEndColumn(); + String error_after = null; + boolean EOFSeen = false; + try { input_stream.readChar(); input_stream.backup(1); } + catch (java.io.IOException e1) { + EOFSeen = true; + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + if (curChar == '\n' || curChar == '\r') { + error_line++; + error_column = 0; + } + else + error_column++; + } + if (!EOFSeen) { + input_stream.backup(1); + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + } + throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR); + } +} + +void TokenLexicalActions(Token matchedToken) +{ + switch(jjmatchedKind) + { + case 2 : + image.append(jjstrLiteralImages[2]); + lengthOfMatch = jjstrLiteralImages[2].length(); + stack.push(DEFAULT); + break; + case 3 : + image.append(jjstrLiteralImages[3]); + lengthOfMatch = jjstrLiteralImages[3].length(); + stack.push(DEFAULT); + break; + case 9 : + image.append(jjstrLiteralImages[9]); + lengthOfMatch = jjstrLiteralImages[9].length(); + stack.push(curLexState); + break; + case 10 : + image.append(jjstrLiteralImages[10]); + lengthOfMatch = jjstrLiteralImages[10].length(); + SwitchTo(stack.pop()); + break; + default : + break; + } +} +private void jjCheckNAdd(int state) +{ + if (jjrounds[state] != jjround) + { + jjstateSet[jjnewStateCnt++] = state; + jjrounds[state] = jjround; + } +} +private void jjAddStates(int start, int end) +{ + do { + jjstateSet[jjnewStateCnt++] = jjnextStates[start]; + } while (start++ != end); +} +private void jjCheckNAddTwoStates(int state1, int state2) +{ + jjCheckNAdd(state1); + jjCheckNAdd(state2); +} + +private void jjCheckNAddStates(int start, int end) +{ + do { + jjCheckNAdd(jjnextStates[start]); + } while (start++ != end); +} + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParserTreeConstants.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParserTreeConstants.java new file mode 100644 index 000000000..db75e5ac3 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ELParserTreeConstants.java @@ -0,0 +1,95 @@ +/* Generated By:JavaCC: Do not edit this line. ELParserTreeConstants.java Version 5.0 */ +package com.sun.el.parser; + +public interface ELParserTreeConstants +{ + public int JJTCOMPOSITEEXPRESSION = 0; + public int JJTLITERALEXPRESSION = 1; + public int JJTDEFERREDEXPRESSION = 2; + public int JJTDYNAMICEXPRESSION = 3; + public int JJTVOID = 4; + public int JJTSEMICOLON = 5; + public int JJTASSIGN = 6; + public int JJTLAMBDAEXPRESSION = 7; + public int JJTLAMBDAPARAMETERS = 8; + public int JJTCHOICE = 9; + public int JJTOR = 10; + public int JJTAND = 11; + public int JJTEQUAL = 12; + public int JJTNOTEQUAL = 13; + public int JJTLESSTHAN = 14; + public int JJTGREATERTHAN = 15; + public int JJTLESSTHANEQUAL = 16; + public int JJTGREATERTHANEQUAL = 17; + public int JJTCONCAT = 18; + public int JJTPLUS = 19; + public int JJTMINUS = 20; + public int JJTMULT = 21; + public int JJTDIV = 22; + public int JJTMOD = 23; + public int JJTNEGATIVE = 24; + public int JJTNOT = 25; + public int JJTEMPTY = 26; + public int JJTVALUE = 27; + public int JJTDOTSUFFIX = 28; + public int JJTBRACKETSUFFIX = 29; + public int JJTMETHODARGUMENTS = 30; + public int JJTMAPDATA = 31; + public int JJTMAPENTRY = 32; + public int JJTLISTDATA = 33; + public int JJTIDENTIFIER = 34; + public int JJTFUNCTION = 35; + public int JJTTRUE = 36; + public int JJTFALSE = 37; + public int JJTFLOATINGPOINT = 38; + public int JJTINTEGER = 39; + public int JJTSTRING = 40; + public int JJTNULL = 41; + + + public String[] jjtNodeName = { + "CompositeExpression", + "LiteralExpression", + "DeferredExpression", + "DynamicExpression", + "void", + "SemiColon", + "Assign", + "LambdaExpression", + "LambdaParameters", + "Choice", + "Or", + "And", + "Equal", + "NotEqual", + "LessThan", + "GreaterThan", + "LessThanEqual", + "GreaterThanEqual", + "Concat", + "Plus", + "Minus", + "Mult", + "Div", + "Mod", + "Negative", + "Not", + "Empty", + "Value", + "DotSuffix", + "BracketSuffix", + "MethodArguments", + "MapData", + "MapEntry", + "ListData", + "Identifier", + "Function", + "True", + "False", + "FloatingPoint", + "Integer", + "String", + "Null", + }; +} +/* JavaCC - OriginalChecksum=295bae338407e43a1d349f1ce802614a (do not edit this line) */ diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/JJTELParserState.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/JJTELParserState.java new file mode 100644 index 000000000..cdb879d45 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/JJTELParserState.java @@ -0,0 +1,123 @@ +/* Generated By:JavaCC: Do not edit this line. JJTELParserState.java Version 5.0 */ +package com.sun.el.parser; + +public class JJTELParserState { + private java.util.List nodes; + private java.util.List marks; + + private int sp; // number of nodes on stack + private int mk; // current mark + private boolean node_created; + + public JJTELParserState() { + nodes = new java.util.ArrayList(); + marks = new java.util.ArrayList(); + sp = 0; + mk = 0; + } + + /* Determines whether the current node was actually closed and + pushed. This should only be called in the final user action of a + node scope. */ + public boolean nodeCreated() { + return node_created; + } + + /* Call this to reinitialize the node stack. It is called + automatically by the parser's ReInit() method. */ + public void reset() { + nodes.clear(); + marks.clear(); + sp = 0; + mk = 0; + } + + /* Returns the root node of the AST. It only makes sense to call + this after a successful parse. */ + public Node rootNode() { + return nodes.get(0); + } + + /* Pushes a node on to the stack. */ + public void pushNode(Node n) { + nodes.add(n); + ++sp; + } + + /* Returns the node on the top of the stack, and remove it from the + stack. */ + public Node popNode() { + if (--sp < mk) { + mk = marks.remove(marks.size()-1); + } + return nodes.remove(nodes.size()-1); + } + + /* Returns the node currently on the top of the stack. */ + public Node peekNode() { + return nodes.get(nodes.size()-1); + } + + /* Returns the number of children on the stack in the current node + scope. */ + public int nodeArity() { + return sp - mk; + } + + + public void clearNodeScope(Node n) { + while (sp > mk) { + popNode(); + } + mk = marks.remove(marks.size()-1); + } + + + public void openNodeScope(Node n) { + marks.add(mk); + mk = sp; + n.jjtOpen(); + } + + + /* A definite node is constructed from a specified number of + children. That number of nodes are popped from the stack and + made the children of the definite node. Then the definite node + is pushed on to the stack. */ + public void closeNodeScope(Node n, int num) { + mk = marks.remove(marks.size()-1); + while (num-- > 0) { + Node c = popNode(); + c.jjtSetParent(n); + n.jjtAddChild(c, num); + } + n.jjtClose(); + pushNode(n); + node_created = true; + } + + + /* A conditional node is constructed if its condition is true. All + the nodes that have been pushed since the node was opened are + made children of the conditional node, which is then pushed + on to the stack. If the condition is false the node is not + constructed and they are left on the stack. */ + public void closeNodeScope(Node n, boolean condition) { + if (condition) { + int a = nodeArity(); + mk = marks.remove(marks.size()-1); + while (a-- > 0) { + Node c = popNode(); + c.jjtSetParent(n); + n.jjtAddChild(c, a); + } + n.jjtClose(); + pushNode(n); + node_created = true; + } else { + mk = marks.remove(marks.size()-1); + node_created = false; + } + } +} +/* JavaCC - OriginalChecksum=a169ec9bf66edaa6db0c5550b112beee (do not edit this line) */ diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/Node.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/Node.java new file mode 100644 index 000000000..da2b7323c --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/Node.java @@ -0,0 +1,98 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; +import javax.el.MethodInfo; +import javax.el.ValueReference; + +import com.sun.el.lang.EvaluationContext; + +/* All AST nodes must implement this interface. It provides basic + machinery for constructing the parent and child relationships + between nodes. */ + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public interface Node { + + /** This method is called after the node has been made the current + node. It indicates that child nodes can now be added to it. */ + public void jjtOpen(); + + /** This method is called after all the child nodes have been + added. */ + public void jjtClose(); + + /** This pair of methods are used to inform the node of its + parent. */ + public void jjtSetParent(Node n); + public Node jjtGetParent(); + + /** This method tells the node to add its argument to the node's + list of children. */ + public void jjtAddChild(Node n, int i); + + /** This method returns a child node. The children are numbered + from zero, left to right. */ + public Node jjtGetChild(int i); + + /** Return the number of children the node has. */ + public int jjtGetNumChildren(); + + public String getImage(); + + public Object getValue(EvaluationContext ctx) throws ELException; + public void setValue(EvaluationContext ctx, Object value) throws ELException; + public Class getType(EvaluationContext ctx) throws ELException; + public ValueReference getValueReference(EvaluationContext ctx) + throws ELException; + public boolean isReadOnly(EvaluationContext ctx) throws ELException; + public void accept(NodeVisitor visitor) throws ELException; + public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException; + public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException; + + public boolean equals(Object n); + public int hashCode(); + public boolean isParametersProvided(); +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/NodeVisitor.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/NodeVisitor.java new file mode 100644 index 000000000..a8294cb8e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/NodeVisitor.java @@ -0,0 +1,51 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public interface NodeVisitor { + public void visit(Node node) throws ELException; +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/ParseException.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ParseException.java new file mode 100644 index 000000000..938471340 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/ParseException.java @@ -0,0 +1,231 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +/** + * This exception is thrown when parse errors are encountered. + * You can explicitly create objects of this exception type by + * calling the method generateParseException in the generated + * parser. + * + * You can modify this class to customize your error reporting + * mechanisms so long as you retain the public fields. + */ +public class ParseException extends Exception { + + /** + * This constructor is used by the method "generateParseException" + * in the generated parser. Calling this constructor generates + * a new object of this type with the fields "currentToken", + * "expectedTokenSequences", and "tokenImage" set. The boolean + * flag "specialConstructor" is also set to true to indicate that + * this constructor was used to create this object. + * This constructor calls its super class with the empty string + * to force the "toString" method of parent class "Throwable" to + * print the error message in the form: + * ParseException: + */ + public ParseException(Token currentTokenVal, + int[][] expectedTokenSequencesVal, + String[] tokenImageVal + ) + { + super(""); + specialConstructor = true; + currentToken = currentTokenVal; + expectedTokenSequences = expectedTokenSequencesVal; + tokenImage = tokenImageVal; + } + + /** + * The following constructors are for use by you for whatever + * purpose you can think of. Constructing the exception in this + * manner makes the exception behave in the normal way - i.e., as + * documented in the class "Throwable". The fields "errorToken", + * "expectedTokenSequences", and "tokenImage" do not contain + * relevant information. The JavaCC generated code does not use + * these constructors. + */ + + public ParseException() { + super(); + specialConstructor = false; + } + + public ParseException(String message) { + super(message); + specialConstructor = false; + } + + /** + * This variable determines which constructor was used to create + * this object and thereby affects the semantics of the + * "getMessage" method (see below). + */ + protected boolean specialConstructor; + + /** + * This is the last token that has been consumed successfully. If + * this object has been created due to a parse error, the token + * followng this token will (therefore) be the first error token. + */ + public Token currentToken; + + /** + * Each entry in this array is an array of integers. Each array + * of integers represents a sequence of tokens (by their ordinal + * values) that is expected at this point of the parse. + */ + public int[][] expectedTokenSequences; + + /** + * This is a reference to the "tokenImage" array of the generated + * parser within which the parse error occurred. This array is + * defined in the generated ...Constants interface. + */ + public String[] tokenImage; + + /** + * This method has the standard behavior when this object has been + * created using the standard constructors. Otherwise, it uses + * "currentToken" and "expectedTokenSequences" to generate a parse + * error message and returns it. If this object has been created + * due to a parse error, and you do not catch it (it gets thrown + * from the parser), then this method is called during the printing + * of the final stack trace, and hence the correct error message + * gets displayed. + */ + public String getMessage() { + if (!specialConstructor) { + return super.getMessage(); + } + String expected = ""; + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + for (int j = 0; j < expectedTokenSequences[i].length; j++) { + expected += tokenImage[expectedTokenSequences[i][j]] + " "; + } + if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) { + expected += "..."; + } + expected += eol + " "; + } + String retval = "Encountered \""; + Token tok = currentToken.next; + for (int i = 0; i < maxSize; i++) { + if (i != 0) retval += " "; + if (tok.kind == 0) { + retval += tokenImage[0]; + break; + } + retval += add_escapes(tok.image); + tok = tok.next; + } + retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; + retval += "." + eol; + if (expectedTokenSequences.length == 1) { + retval += "Was expecting:" + eol + " "; + } else { + retval += "Was expecting one of:" + eol + " "; + } + retval += expected; + return retval; + } + + /** + * The end of line string for this machine. + */ + protected String eol = System.getProperty("line.separator", "\n"); + + /** + * Used to convert raw characters to their escaped version + * when these raw version cannot be used as part of an ASCII + * string literal. + */ + protected String add_escapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/SimpleCharStream.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/SimpleCharStream.java new file mode 100644 index 000000000..32d9c8d7b --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/SimpleCharStream.java @@ -0,0 +1,471 @@ +/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 5.0 */ +/* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package com.sun.el.parser; + +/** + * An implementation of interface CharStream, where the stream is assumed to + * contain only ASCII characters (without unicode processing). + */ + +public class SimpleCharStream +{ +/** Whether parser is static. */ + public static final boolean staticFlag = false; + int bufsize; + int available; + int tokenBegin; +/** Position in buffer. */ + public int bufpos = -1; + protected int bufline[]; + protected int bufcolumn[]; + + protected int column = 0; + protected int line = 1; + + protected boolean prevCharIsCR = false; + protected boolean prevCharIsLF = false; + + protected java.io.Reader inputStream; + + protected char[] buffer; + protected int maxNextCharInd = 0; + protected int inBuf = 0; + protected int tabSize = 8; + + protected void setTabSize(int i) { tabSize = i; } + protected int getTabSize(int i) { return tabSize; } + + + protected void ExpandBuff(boolean wrapAround) + { + char[] newbuffer = new char[bufsize + 2048]; + int newbufline[] = new int[bufsize + 2048]; + int newbufcolumn[] = new int[bufsize + 2048]; + + try + { + if (wrapAround) + { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos += (bufsize - tokenBegin)); + } + else + { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos -= tokenBegin); + } + } + catch (Throwable t) + { + throw new Error(t.getMessage()); + } + + + bufsize += 2048; + available = bufsize; + tokenBegin = 0; + } + + protected void FillBuff() throws java.io.IOException + { + if (maxNextCharInd == available) + { + if (available == bufsize) + { + if (tokenBegin > 2048) + { + bufpos = maxNextCharInd = 0; + available = tokenBegin; + } + else if (tokenBegin < 0) + bufpos = maxNextCharInd = 0; + else + ExpandBuff(false); + } + else if (available > tokenBegin) + available = bufsize; + else if ((tokenBegin - available) < 2048) + ExpandBuff(true); + else + available = tokenBegin; + } + + int i; + try { + if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1) + { + inputStream.close(); + throw new java.io.IOException(); + } + else + maxNextCharInd += i; + return; + } + catch(java.io.IOException e) { + --bufpos; + backup(0); + if (tokenBegin == -1) + tokenBegin = bufpos; + throw e; + } + } + +/** Start. */ + public char BeginToken() throws java.io.IOException + { + tokenBegin = -1; + char c = readChar(); + tokenBegin = bufpos; + + return c; + } + + protected void UpdateLineColumn(char c) + { + column++; + + if (prevCharIsLF) + { + prevCharIsLF = false; + line += (column = 1); + } + else if (prevCharIsCR) + { + prevCharIsCR = false; + if (c == '\n') + { + prevCharIsLF = true; + } + else + line += (column = 1); + } + + switch (c) + { + case '\r' : + prevCharIsCR = true; + break; + case '\n' : + prevCharIsLF = true; + break; + case '\t' : + column--; + column += (tabSize - (column % tabSize)); + break; + default : + break; + } + + bufline[bufpos] = line; + bufcolumn[bufpos] = column; + } + +/** Read a character. */ + public char readChar() throws java.io.IOException + { + if (inBuf > 0) + { + --inBuf; + + if (++bufpos == bufsize) + bufpos = 0; + + return buffer[bufpos]; + } + + if (++bufpos >= maxNextCharInd) + FillBuff(); + + char c = buffer[bufpos]; + + UpdateLineColumn(c); + return c; + } + + @Deprecated + /** + * @deprecated + * @see #getEndColumn + */ + + public int getColumn() { + return bufcolumn[bufpos]; + } + + @Deprecated + /** + * @deprecated + * @see #getEndLine + */ + + public int getLine() { + return bufline[bufpos]; + } + + /** Get token end column number. */ + public int getEndColumn() { + return bufcolumn[bufpos]; + } + + /** Get token end line number. */ + public int getEndLine() { + return bufline[bufpos]; + } + + /** Get token beginning column number. */ + public int getBeginColumn() { + return bufcolumn[tokenBegin]; + } + + /** Get token beginning line number. */ + public int getBeginLine() { + return bufline[tokenBegin]; + } + +/** Backup a number of characters. */ + public void backup(int amount) { + + inBuf += amount; + if ((bufpos -= amount) < 0) + bufpos += bufsize; + } + + /** Constructor. */ + public SimpleCharStream(java.io.Reader dstream, int startline, + int startcolumn, int buffersize) + { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + } + + /** Constructor. */ + public SimpleCharStream(java.io.Reader dstream, int startline, + int startcolumn) + { + this(dstream, startline, startcolumn, 4096); + } + + /** Constructor. */ + public SimpleCharStream(java.io.Reader dstream) + { + this(dstream, 1, 1, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader dstream, int startline, + int startcolumn, int buffersize) + { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + if (buffer == null || buffersize != buffer.length) + { + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + } + prevCharIsLF = prevCharIsCR = false; + tokenBegin = inBuf = maxNextCharInd = 0; + bufpos = -1; + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader dstream, int startline, + int startcolumn) + { + ReInit(dstream, startline, startcolumn, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader dstream) + { + ReInit(dstream, 1, 1, 4096); + } + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, + int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException + { + this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream, int startline, + int startcolumn, int buffersize) + { + this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, + int startcolumn) throws java.io.UnsupportedEncodingException + { + this(dstream, encoding, startline, startcolumn, 4096); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream, int startline, + int startcolumn) + { + this(dstream, startline, startcolumn, 4096); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException + { + this(dstream, encoding, 1, 1, 4096); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream) + { + this(dstream, 1, 1, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, String encoding, int startline, + int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException + { + ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, int startline, + int startcolumn, int buffersize) + { + ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException + { + ReInit(dstream, encoding, 1, 1, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream) + { + ReInit(dstream, 1, 1, 4096); + } + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, String encoding, int startline, + int startcolumn) throws java.io.UnsupportedEncodingException + { + ReInit(dstream, encoding, startline, startcolumn, 4096); + } + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, int startline, + int startcolumn) + { + ReInit(dstream, startline, startcolumn, 4096); + } + /** Get token literal value. */ + public String GetImage() + { + if (bufpos >= tokenBegin) + return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); + else + return new String(buffer, tokenBegin, bufsize - tokenBegin) + + new String(buffer, 0, bufpos + 1); + } + + /** Get the suffix. */ + public char[] GetSuffix(int len) + { + char[] ret = new char[len]; + + if ((bufpos + 1) >= len) + System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); + else + { + System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, + len - bufpos - 1); + System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); + } + + return ret; + } + + /** Reset buffer when finished. */ + public void Done() + { + buffer = null; + bufline = null; + bufcolumn = null; + } + + /** + * Method to adjust line and column numbers for the start of a token. + */ + public void adjustBeginLineColumn(int newLine, int newCol) + { + int start = tokenBegin; + int len; + + if (bufpos >= tokenBegin) + { + len = bufpos - tokenBegin + inBuf + 1; + } + else + { + len = bufsize - tokenBegin + bufpos + 1 + inBuf; + } + + int i = 0, j = 0, k = 0; + int nextColDiff = 0, columnDiff = 0; + + while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) + { + bufline[j] = newLine; + nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; + bufcolumn[j] = newCol + columnDiff; + columnDiff = nextColDiff; + i++; + } + + if (i < len) + { + bufline[j] = newLine++; + bufcolumn[j] = newCol + columnDiff; + + while (i++ < len) + { + if (bufline[j = start % bufsize] != bufline[++start % bufsize]) + bufline[j] = newLine++; + else + bufline[j] = newLine; + } + } + + line = bufline[j]; + column = bufcolumn[j]; + } + +} +/* JavaCC - OriginalChecksum=7ea14199259e7ce0336b228c8cdb9958 (do not edit this line) */ diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/SimpleNode.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/SimpleNode.java new file mode 100644 index 000000000..08b8c437c --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/SimpleNode.java @@ -0,0 +1,244 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import javax.el.ELException; +import javax.el.MethodInfo; +import javax.el.ValueReference; +import javax.el.PropertyNotWritableException; + +import com.sun.el.lang.ELSupport; +import com.sun.el.lang.EvaluationContext; +import com.sun.el.util.MessageFactory; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public abstract class SimpleNode extends ELSupport implements Node { + protected Node parent; + + protected Node[] children; + + protected int id; + + protected String image; + + public SimpleNode(int i) { + id = i; + } + + public void jjtOpen() { + } + + public void jjtClose() { + } + + public void jjtSetParent(Node n) { + parent = n; + } + + public Node jjtGetParent() { + return parent; + } + + public void jjtAddChild(Node n, int i) { + if (children == null) { + children = new Node[i + 1]; + } else if (i >= children.length) { + Node c[] = new Node[i + 1]; + System.arraycopy(children, 0, c, 0, children.length); + children = c; + } + children[i] = n; + } + + public Node jjtGetChild(int i) { + return children[i]; + } + + public int jjtGetNumChildren() { + return (children == null) ? 0 : children.length; + } + + /* + * You can override these two methods in subclasses of SimpleNode to + * customize the way the node appears when the tree is dumped. If your + * output uses more than one line you should override toString(String), + * otherwise overriding toString() is probably all you need to do. + */ + + public String toString() { + if (this.image != null) { + return ELParserTreeConstants.jjtNodeName[id] + "[" + this.image + + "]"; + } + return ELParserTreeConstants.jjtNodeName[id]; + } + + public String toString(String prefix) { + return prefix + toString(); + } + + /* + * Override this method if you want to customize how the node dumps out its + * children. + */ + + public void dump(String prefix) { + System.out.println(toString(prefix)); + if (children != null) { + for (int i = 0; i < children.length; ++i) { + SimpleNode n = (SimpleNode) children[i]; + if (n != null) { + n.dump(prefix + " "); + } + } + } + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public Class getType(EvaluationContext ctx) + throws ELException { + throw new UnsupportedOperationException(); + } + + public Object getValue(EvaluationContext ctx) + throws ELException { + throw new UnsupportedOperationException(); + } + + public ValueReference getValueReference(EvaluationContext ctx) + throws ELException { + return null; + } + + public boolean isReadOnly(EvaluationContext ctx) + throws ELException { + return true; + } + + public void setValue(EvaluationContext ctx, Object value) + throws ELException { + throw new PropertyNotWritableException(MessageFactory.get("error.syntax.set")); + } + + public void accept(NodeVisitor visitor) throws ELException { + visitor.visit(this); + if (this.children != null && this.children.length > 0) { + for (int i = 0; i < this.children.length; i++) { + this.children[i].accept(visitor); + } + } + } + + public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException { + throw new UnsupportedOperationException(); + } + + public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object node) { + if (! (node instanceof SimpleNode)) { + return false; + } + SimpleNode n = (SimpleNode) node; + if (this.id != n.id) { + return false; + } + if (this.children == null && n.children == null) { + if (this.image == null) { + return n.image == null; + } + return this.image.equals(n.image); + } + if (this.children == null || n.children == null) { + // One is null and the other is non-null + return false; + } + if (this.children.length != n.children.length) { + return false; + } + if (this.children.length == 0) { + if (this.image == null) { + return n.image == null; + } + return this.image.equals(n.image); + } + for (int i = 0; i < this.children.length; i++) { + if (! this.children[i].equals(n.children[i])) { + return false; + } + } + return true; + } + + @Override + public boolean isParametersProvided() { + return false; + } + + @Override + public int hashCode() { + if (this.children == null || this.children.length == 0) { + if (this.image != null) { + return this.image.hashCode(); + } + return this.id; + } + int h = 0; + for (int i = this.children.length - 1; i >=0; i--) { + h = h + h + h + this.children[i].hashCode(); + } + h = h + h + h + id; + return h; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/Token.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/Token.java new file mode 100644 index 000000000..e310a252c --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/Token.java @@ -0,0 +1,122 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +import java.io.Serializable; + +/** + * Describes the input token stream. + */ + +public class Token implements Serializable { + + /** + * An integer that describes the kind of this token. This numbering + * system is determined by JavaCCParser, and a table of these numbers is + * stored in the file ...Constants.java. + */ + public int kind; + + /** + * beginLine and beginColumn describe the position of the first character + * of this token; endLine and endColumn describe the position of the + * last character of this token. + */ + public int beginLine, beginColumn, endLine, endColumn; + + /** + * The string image of the token. + */ + public String image; + + /** + * A reference to the next regular (non-special) token from the input + * stream. If this is the last token from the input stream, or if the + * token manager has not read tokens beyond this one, this field is + * set to null. This is true only if this token is also a regular + * token. Otherwise, see below for a description of the contents of + * this field. + */ + public Token next; + + /** + * This field is used to access special tokens that occur prior to this + * token, but after the immediately preceding regular (non-special) token. + * If there are no such special tokens, this field is set to null. + * When there are more than one such special token, this field refers + * to the last of these special tokens, which in turn refers to the next + * previous special token through its specialToken field, and so on + * until the first special token (whose specialToken field is null). + * The next fields of special tokens refer to other special tokens that + * immediately follow it (without an intervening regular token). If there + * is no such token, this field is null. + */ + public Token specialToken; + + /** + * Returns the image. + */ + public String toString() + { + return image; + } + + /** + * Returns a new Token object, by default. However, if you want, you + * can create and return subclass objects based on the value of ofKind. + * Simply add the cases to the switch for all those special cases. + * For example, if you have a subclass of Token called IDToken that + * you want to create if ofKind is ID, simlpy add something like : + * + * case MyParserConstants.ID : return new IDToken(); + * + * to the following switch statement. Then you can cast matchedToken + * variable to the appropriate type and use it in your lexical actions. + */ + public static final Token newToken(int ofKind) + { + switch(ofKind) + { + default : return new Token(); + } + } + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/parser/TokenMgrError.java b/fine-third-default/fine-javax-el/src/com/sun/el/parser/TokenMgrError.java new file mode 100644 index 000000000..f51dfc590 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/parser/TokenMgrError.java @@ -0,0 +1,172 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.parser; + +public class TokenMgrError extends Error +{ + /* + * Ordinals for various reasons why an Error of this type can be thrown. + */ + + /** + * Lexical error occured. + */ + static final int LEXICAL_ERROR = 0; + + /** + * An attempt wass made to create a second instance of a static token manager. + */ + static final int STATIC_LEXER_ERROR = 1; + + /** + * Tried to change to an invalid lexical state. + */ + static final int INVALID_LEXICAL_STATE = 2; + + /** + * Detected (and bailed out of) an infinite loop in the token manager. + */ + static final int LOOP_DETECTED = 3; + + /** + * Indicates the reason why the exception is thrown. It will have + * one of the above 4 values. + */ + int errorCode; + + /** + * Replaces unprintable characters by their espaced (or unicode escaped) + * equivalents in the given string + */ + protected static final String addEscapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + + /** + * Returns a detailed message for the Error when it is thrown by the + * token manager to indicate a lexical error. + * Parameters : + * EOFSeen : indicates if EOF caused the lexicl error + * curLexState : lexical state in which this error occured + * errorLine : line number when the error occured + * errorColumn : column number when the error occured + * errorAfter : prefix that was seen before this error occured + * curchar : the offending character + * Note: You can customize the lexical error message by modifying this method. + */ + protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) { + return("Lexical error at line " + + errorLine + ", column " + + errorColumn + ". Encountered: " + + (EOFSeen ? " " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") + + "after : \"" + addEscapes(errorAfter) + "\""); + } + + /** + * You can also modify the body of this method to customize your error messages. + * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not + * of end-users concern, so you can return something like : + * + * "Internal Error : Please file a bug report .... " + * + * from this method for such cases in the release version of your parser. + */ + public String getMessage() { + return super.getMessage(); + } + + /* + * Constructors of various flavors follow. + */ + + public TokenMgrError() { + } + + public TokenMgrError(String message, int reason) { + super(message); + errorCode = reason; + } + + public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) { + this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/stream/Operator.java b/fine-third-default/fine-javax-el/src/com/sun/el/stream/Operator.java new file mode 100644 index 000000000..327348bef --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/stream/Operator.java @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * @author Kin-man Chung + */ + +package com.sun.el.stream; + +import java.util.Iterator; + +interface Operator { + + Iterator iterator(Iterator upstream); +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/stream/Optional.java b/fine-third-default/fine-javax-el/src/com/sun/el/stream/Optional.java new file mode 100644 index 000000000..3dd67f865 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/stream/Optional.java @@ -0,0 +1,91 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * @author Kin-man Chung + */ + +package com.sun.el.stream; + +import java.util.Iterator; +import java.util.Comparator; + +import javax.el.ELContext; +import javax.el.LambdaExpression; + +public class Optional { + + private final static Optional EMPTY = new Optional(); + private final Object value; + + Optional(Object value) { + if (value == null) { + throw new NullPointerException(); + } + this.value = value; + } + + Optional() { + this.value = null; + } + + public boolean isPresent() { + return value != null; + } + + public void ifPresent(LambdaExpression lambda) { + if (value != null) { + lambda.invoke(value); + } + } + + public Object get() { + if (value == null) { + throw new java.util.NoSuchElementException("No value present"); + } + return value; + } + + public Object orElse(Object other) { + return value != null? value: other; + } + + public Object orElseGet(LambdaExpression other) { + return value != null? value: other.invoke(); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/stream/Stream.java b/fine-third-default/fine-javax-el/src/com/sun/el/stream/Stream.java new file mode 100644 index 000000000..bcb0d7143 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/stream/Stream.java @@ -0,0 +1,590 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * @author Kin-man Chung + */ + +package com.sun.el.stream; + +import com.sun.el.lang.ELSupport; +import com.sun.el.lang.ELArithmetic; + +import java.lang.reflect.Array; +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.Set; + +import javax.el.ELException; +import javax.el.LambdaExpression; + + +/* + */ + +public class Stream { + + private Iterator source; + private Stream upstream; + private Operator op; + + Stream(Iterator source) { + this.source = source; + } + + Stream(Stream upstream, Operator op) { + this.upstream = upstream; + this.op = op; + } + + public Iterator iterator() { + if (source != null) { + return source; + } + + return op.iterator(upstream.iterator()); + } + + public Stream filter(final LambdaExpression predicate) { + return new Stream(this, new Operator() { + @Override + public Iterator iterator(final Iterator upstream) { + return new Iterator2(upstream) { + @Override + public void doItem(Object item) { + if ((Boolean) predicate.invoke(item)) { + yield(item); + } + } + }; + } + }); + } + + public Stream map(final LambdaExpression mapper) { + return new Stream(this, new Operator() { + @Override + public Iterator iterator(final Iterator up) { + return new Iterator1(up) { + @Override + public Object next() { + return mapper.invoke(iter.next()); + } + }; + } + }); + } + + public Stream peek(final LambdaExpression comsumer) { + return new Stream(this, new Operator() { + @Override + public Iterator iterator(final Iterator up) { + return new Iterator2(up) { + @Override + void doItem(Object item){ + comsumer.invoke(item); + yield(item); + } + }; + } + }); + } + + public Stream limit(final long n) { + if (n < 0) { + throw new IllegalArgumentException("limit must be non-negative"); + } + return new Stream(this, new Operator() { + @Override + public Iterator iterator(final Iterator up) { + return new Iterator0() { + long limit = n; + @Override + public boolean hasNext() { + return (limit > 0)? up.hasNext(): false; + } + @Override + public Object next() { + limit--; + return up.next(); + } + }; + } + }); + } + + public Stream substream(final long startIndex) { + if (startIndex < 0) { + throw new IllegalArgumentException("substream index must be non-negative"); + } + return new Stream(this, new Operator() { + long skip = startIndex; + @Override + public Iterator iterator(final Iterator up) { + while (skip > 0 && up.hasNext()) { + up.next(); + skip--; + } + return up; + } + }); + } + + public Stream substream(long startIndex, long endIndex) { + return substream(startIndex).limit(endIndex-startIndex); + } + + public Stream distinct () { + return new Stream(this, new Operator() { + @Override + public Iterator iterator(final Iterator up) { + return new Iterator2(up) { + private Set set = new HashSet(); + @Override + public void doItem(Object item) { + if (set.add(item)) { + yield(item); + } + } + }; + } + }); + } + + public Stream sorted() { + return new Stream(this, new Operator() { + + private PriorityQueue queue = null; + + @Override + public Iterator iterator(final Iterator up) { + if (queue == null) { + queue = new PriorityQueue(16, + new Comparator() { + @Override + public int compare(Object o1, Object o2) { + return ((Comparable)o1).compareTo(o2); + } + }); + + while(up.hasNext()) { + queue.add(up.next()); + } + } + + return new Iterator0() { + @Override + public boolean hasNext() { + return !queue.isEmpty(); + } + @Override + public Object next() { + return queue.remove(); + } + }; + } + }); + } + + public Stream sorted(final LambdaExpression comparator) { + return new Stream(this, new Operator() { + + private PriorityQueue queue = null; + + @Override + public Iterator iterator(final Iterator up) { + if (queue == null) { + queue = new PriorityQueue(16, + new Comparator() { + @Override + public int compare(Object o1, Object o2) { + return (Integer) ELSupport.coerceToType( + comparator.invoke(o1, o2), + Integer.class); + } + }); + + while(up.hasNext()) { + queue.add(up.next()); + } + } + + return new Iterator0() { + @Override + public boolean hasNext() { + return !queue.isEmpty(); + } + @Override + public Object next() { + return queue.remove(); + } + }; + } + }); + } + + public Stream flatMap(final LambdaExpression mapper) { + return new Stream(this, new Operator() { + @Override + public Iterator iterator(final Iterator upstream) { + return new Iterator0() { + Iterator iter = null; + @Override + public boolean hasNext() { + while (true) { + if (iter == null) { + if (!upstream.hasNext()) { + return false; + } + Object mapped = mapper.invoke(upstream.next()); + if (! (mapped instanceof Stream)) { + throw new ELException("Expecting a Stream " + + "from flatMap's mapper function."); + } + iter = ((Stream)mapped).iterator(); + } + else { + if (iter.hasNext()) { + return true; + } + iter = null; + } + } + } + @Override + public Object next() { + if (iter == null) { + return null; + } + return iter.next(); + } + }; + } + }); + } + + public Object reduce(Object base, LambdaExpression op) { + Iterator iter = iterator(); + while (iter.hasNext()) { + base = op.invoke(base, iter.next()); + } + return base; + } + + public Optional reduce(LambdaExpression op) { + Iterator iter = iterator(); + if (iter.hasNext()) { + Object base = iter.next(); + while (iter.hasNext()) { + base = op.invoke(base, iter.next()); + } + return new Optional(base); + } + return new Optional(); + } + +/* + public Map reduceBy(LambdaExpression classifier, + LambdaExpression seed, + LambdaExpression reducer) { + Map map = new HashMap(); + Iterator iter = iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + Object key = classifier.invoke(item); + Object value = map.get(key); + if (value == null) { + value = seed.invoke(); + } + map.put(key, reducer.invoke(value, item)); + } + return map; + } +*/ + + public void forEach(LambdaExpression comsumer) { + Iterator iter = iterator(); + while (iter.hasNext()) { + comsumer.invoke(iter.next()); + } + } + +/* + public Map> groupBy(LambdaExpression classifier) { + Map> map = + new HashMap>(); + Iterator iter = iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + Object key = classifier.invoke(item); + if (key == null) { + throw new ELException("null key"); + } + Collection c = map.get(key); + if (c == null) { + c = new ArrayList(); + map.put(key, c); + } + c.add(item); + } + return map; + } +*/ + public boolean anyMatch(LambdaExpression predicate) { + Iterator iter = iterator(); + while (iter.hasNext()) { + if ((Boolean) predicate.invoke(iter.next())) { + return true; + } + } + return false; + } + + public boolean allMatch(LambdaExpression predicate) { + Iterator iter = iterator(); + while (iter.hasNext()) { + if (! (Boolean) predicate.invoke(iter.next())) { + return false; + } + } + return true; + } + + public boolean noneMatch(LambdaExpression predicate) { + Iterator iter = iterator(); + while (iter.hasNext()) { + if ((Boolean) predicate.invoke(iter.next())) { + return false; + } + } + return true; + } + + public Object[] toArray() { + Iterator iter = iterator(); + ArrayList al = new ArrayList(); + while (iter.hasNext()) { + al.add(iter.next()); + } + return al.toArray(); + } + + public Object toList() { + Iterator iter = iterator(); + ArrayList al = new ArrayList(); + while (iter.hasNext()) { + al.add(iter.next()); + } + return al; + } + +/* + public Object into(Object target) { + if (! (target instanceof Collection)) { + throw new ELException("The argument type for into operation mush be a Collection"); + } + Collection c = (Collection) target; + Iterator iter = iterator(); + while (iter.hasNext()) { + c.add(iter.next()); + } + return c; + } +*/ + + public Optional findFirst() { + Iterator iter = iterator(); + if (iter.hasNext()) { + return new Optional(iter.next()); + } else { + return new Optional(); + } + } + + public Object sum() { + Number sum = Long.valueOf(0); + Iterator iter = iterator(); + while (iter.hasNext()) { + sum = ELArithmetic.add(sum, iter.next()); + } + return sum; + } + + public Object count() { + long count = 0; + Iterator iter = iterator(); + while (iter.hasNext()) { + count++; + iter.next(); + } + return Long.valueOf(count); + } + + public Optional min() { + Object min = null; + Iterator iter = iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + if (min == null || ELSupport.compare(min, item) > 0) { + min = item; + } + } + if (min == null) { + return new Optional(); + } + return new Optional(min); + } + + public Optional max() { + Object max = null; + Iterator iter = iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + if (max == null || ELSupport.compare(max, item) < 0) { + max = item; + } + } + if (max == null) { + return new Optional(); + } + return new Optional(max); + } + + public Optional min(final LambdaExpression comparator) { + Object min = null; + Iterator iter = iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + if (min == null || + ELSupport.compare(comparator.invoke(item, min), Long.valueOf(0)) < 0) { + min = item; + } + } + if (min == null) { + return new Optional(); + } + return new Optional(min); + } + + public Optional max(final LambdaExpression comparator) { + Object max = null; + Iterator iter = iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + if (max == null || + ELSupport.compare(comparator.invoke(max, item), Long.valueOf(0)) < 0) { + max = item; + } + } + if (max == null) { + return new Optional(); + } + return new Optional(max); + } + + public Optional average() { + Number sum = Long.valueOf(0); + long count = 0; + Iterator iter = iterator(); + while (iter.hasNext()) { + count++; + sum = ELArithmetic.add(sum, iter.next()); + } + if (count == 0) { + return new Optional(); + } + return new Optional(ELArithmetic.divide(sum, count)); + } + + abstract class Iterator0 implements Iterator { + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + abstract class Iterator1 extends Iterator0 { + + Iterator iter; + Iterator1(Iterator iter) { + this.iter = iter; + } + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + } + + abstract class Iterator2 extends Iterator1 { + private Object current; + private boolean yielded; + + Iterator2(Iterator upstream) { + super(upstream); + } + + @Override + public Object next() { + yielded = false; + return current; + } + + @Override + public boolean hasNext() { + while ((!yielded) && iter.hasNext()) { + doItem(iter.next()); + } + return yielded; + } + + void yield(Object current) { + this.current = current; + yielded = true; + } + + abstract void doItem(Object item); + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/stream/StreamELResolver.java b/fine-third-default/fine-javax-el/src/com/sun/el/stream/StreamELResolver.java new file mode 100644 index 000000000..9d5d493bf --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/stream/StreamELResolver.java @@ -0,0 +1,155 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * @author Kin-man Chung + */ + +package com.sun.el.stream; + +import java.beans.FeatureDescriptor; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.lang.reflect.Array; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.ELResolver; +import javax.el.LambdaExpression; + +/* + * This ELResolver intercepts method calls to a Collections, to provide + * support for collection operations. + */ + +public class StreamELResolver extends ELResolver { + + public Object invoke(final ELContext context, + final Object base, + final Object method, + final Class[] paramTypes, + final Object[] params) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof Collection) { + @SuppressWarnings("unchecked") + Collection c = (Collection)base; + if ("stream".equals(method) && params.length == 0) { + context.setPropertyResolved(true); + return new Stream(c.iterator()); + } + } + if (base.getClass().isArray()) { + if ("stream".equals(method) && params.length == 0) { + context.setPropertyResolved(true); + return new Stream(arrayIterator(base)); + } + } + return null; + } + + private static Iterator arrayIterator(final Object base) { + final int size = Array.getLength(base); + return new Iterator() { + int index = 0; + boolean yielded; + Object current; + + @Override + public boolean hasNext() { + if ((!yielded) && index < size) { + current = Array.get(base, index++); + yielded = true; + } + return yielded; + } + + @Override + public Object next() { + yielded = false; + return current; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + +/* + private LambdaExpression getLambda(Object obj, String method) { + if (obj == null || ! (obj instanceof LambdaExpression)) { + throw new ELException ("When calling " + method + ", expecting an " + + "EL lambda expression, but found " + obj); + } + return (LambdaExpression) obj; + } +*/ + public Object getValue(ELContext context, Object base, Object property) { + return null; + } + + public Class getType(ELContext context, Object base, Object property) { + return null; + } + + public void setValue(ELContext context, Object base, Object property, + Object value) { + } + + public boolean isReadOnly(ELContext context, Object base, Object property) { + return false; + } + + public Iterator getFeatureDescriptors( + ELContext context, + Object base) { + return null; + } + + public Class getCommonPropertyType(ELContext context, Object base) { + return String.class; + } +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/util/MessageFactory.java b/fine-third-default/fine-javax-el/src/com/sun/el/util/MessageFactory.java new file mode 100644 index 000000000..037889765 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/util/MessageFactory.java @@ -0,0 +1,94 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.util; + +import java.text.MessageFormat; +import java.util.ResourceBundle; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class MessageFactory { + + protected final static ResourceBundle bundle = ResourceBundle + .getBundle("com.sun.el.Messages"); + /** + * + */ + public MessageFactory() { + super(); + } + + public static String get(final String key) { + return bundle.getString(key); + } + + public static String get(final String key, final Object obj0) { + return getArray(key, new Object[] { obj0 }); + } + + public static String get(final String key, final Object obj0, + final Object obj1) { + return getArray(key, new Object[] { obj0, obj1 }); + } + + public static String get(final String key, final Object obj0, + final Object obj1, final Object obj2) { + return getArray(key, new Object[] { obj0, obj1, obj2 }); + } + + public static String get(final String key, final Object obj0, + final Object obj1, final Object obj2, final Object obj3) { + return getArray(key, new Object[] { obj0, obj1, obj2, obj3 }); + } + + public static String get(final String key, final Object obj0, + final Object obj1, final Object obj2, final Object obj3, + final Object obj4) { + return getArray(key, new Object[] { obj0, obj1, obj2, obj3, obj4 }); + } + + public static String getArray(final String key, final Object[] objA) { + return MessageFormat.format(bundle.getString(key), objA); + } + +} diff --git a/fine-third-default/fine-javax-el/src/com/sun/el/util/ReflectionUtil.java b/fine-third-default/fine-javax-el/src/com/sun/el/util/ReflectionUtil.java new file mode 100644 index 000000000..6d2e5e8af --- /dev/null +++ b/fine-third-default/fine-javax-el/src/com/sun/el/util/ReflectionUtil.java @@ -0,0 +1,297 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.el.util; + +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; + +import javax.el.ELException; +import javax.el.MethodNotFoundException; +import javax.el.PropertyNotFoundException; + +import com.sun.el.lang.ELSupport; + +/** + * Utilities for Managing Serialization and Reflection + * + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class ReflectionUtil { + + protected static final String[] EMPTY_STRING = new String[0]; + + protected static final String[] PRIMITIVE_NAMES = new String[] { "boolean", + "byte", "char", "double", "float", "int", "long", "short", "void" }; + + protected static final Class[] PRIMITIVES = new Class[] { boolean.class, + byte.class, char.class, double.class, float.class, int.class, + long.class, short.class, Void.TYPE }; + + /** + * + */ + private ReflectionUtil() { + super(); + } + + public static Class forName(String name) throws ClassNotFoundException { + if (null == name || "".equals(name)) { + return null; + } + Class c = forNamePrimitive(name); + if (c == null) { + if (name.endsWith("[]")) { + String nc = name.substring(0, name.length() - 2); + c = Class.forName(nc, true, Thread.currentThread().getContextClassLoader()); + c = Array.newInstance(c, 0).getClass(); + } else { + c = Class.forName(name, true, Thread.currentThread().getContextClassLoader()); + } + } + return c; + } + + protected static Class forNamePrimitive(String name) { + if (name.length() <= 8) { + int p = Arrays.binarySearch(PRIMITIVE_NAMES, name); + if (p >= 0) { + return PRIMITIVES[p]; + } + } + return null; + } + + /** + * Converts an array of Class names to Class types + * @param s + * @return The array of Classes + * @throws ClassNotFoundException + */ + public static Class[] toTypeArray(String[] s) throws ClassNotFoundException { + if (s == null) + return null; + Class[] c = new Class[s.length]; + for (int i = 0; i < s.length; i++) { + c[i] = forName(s[i]); + } + return c; + } + + /** + * Converts an array of Class types to Class names + * @param c + * @return The array of Classes + */ + public static String[] toTypeNameArray(Class[] c) { + if (c == null) + return null; + String[] s = new String[c.length]; + for (int i = 0; i < c.length; i++) { + s[i] = c[i].getName(); + } + return s; + } + + /** + * Returns a method based on the criteria + * @param base the object that owns the method + * @param property the name of the method + * @param paramTypes the parameter types to use + * @return the method specified + * @throws MethodNotFoundException + */ + public static Method getMethod(Object base, Object property, + Class[] paramTypes) throws MethodNotFoundException { + if (base == null || property == null) { + throw new MethodNotFoundException(MessageFactory.get( + "error.method.notfound", base, property, + paramString(paramTypes))); + } + + String methodName = property.toString(); + + Method method = getMethod(base.getClass(), methodName, paramTypes); + if (method == null) { + throw new MethodNotFoundException(MessageFactory.get( + "error.method.notfound", base, property, + paramString(paramTypes))); + } + return method; + } + + /* + * Get a public method form a public class or interface of a given method. + * Note that if the base is an instance of a non-public class that + * implements a public interface, calling Class.getMethod() with the base + * will not find the method. To correct this, a version of the + * same method must be found in a superclass or interface. + **/ + + static private Method getMethod(Class cl, String methodName, + Class[] paramTypes) { + + Method m = null; + try { + m = cl.getMethod(methodName, paramTypes); + } catch (NoSuchMethodException ex) { + return null; + } + + Class dclass = m.getDeclaringClass(); + if (Modifier.isPublic(dclass.getModifiers())) { + return m; + } + + for (Class c: dclass.getInterfaces()) { + m = getMethod(c, methodName, paramTypes); + if (m != null) { + return m; + } + } + Class c = dclass.getSuperclass(); + if (c != null) { + m = getMethod(c, methodName, paramTypes); + if (m != null) { + return m; + } + } + return null; + } + + protected static final String paramString(Class[] types) { + if (types != null) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < types.length; i++) { + sb.append(types[i].getName()).append(", "); + } + if (sb.length() > 2) { + sb.setLength(sb.length() - 2); + } + return sb.toString(); + } + return null; + } + + /** + * @param base The base object + * @param property The property + * @return The PropertyDescriptor for the base with the given property + * @throws ELException + * @throws PropertyNotFoundException + */ + public static PropertyDescriptor getPropertyDescriptor(Object base, + Object property) throws ELException, PropertyNotFoundException { + String name = ELSupport.coerceToString(property); + PropertyDescriptor p = null; + try { + PropertyDescriptor[] desc = Introspector.getBeanInfo( + base.getClass()).getPropertyDescriptors(); + for (int i = 0; i < desc.length; i++) { + if (desc[i].getName().equals(name)) { + return desc[i]; + } + } + } catch (IntrospectionException ie) { + throw new ELException(ie); + } + throw new PropertyNotFoundException(MessageFactory.get( + "error.property.notfound", base, name)); + } + + /* + * For now, find the first method that matches the name and the parameter + * count. + */ +/* + public static Method findMethod(Object base, Object property, + Object[] params) throws ELException { + + String methodName = property.toString(); + for (Method m: base.getClass().getMethods()) { + if (m.getName().equals(methodName) && ( + m.isVarArgs() || + m.getParameterTypes().length==params.length)){ + return m; + } + } + throw new ELException("Method " + methodName + " not Found"); + } +*/ + + /** + * Invoke a method with parameters. + */ +/* + public static Object invokeMethod(ELContext context, + Object base, Object property, + Object[] params) throws ELException { + + Method m = findMethod(base, property, params); + Class[] parameterTypes = m.getParameterTypes(); + Object[] parameters = null; + if (parameterTypes.length > 0) { + if (m.isVarArgs()) { + // TODO + } else { + parameters = new Object[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + parameters[i] = context.convertToType(params[i], + parameterTypes[i]); + } + } + } + try { + return m.invoke(base, parameters); + } catch (IllegalAccessException iae) { + throw new ELException(iae); + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } + } +*/ +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ArrayELResolver.java b/fine-third-default/fine-javax-el/src/javax/el/ArrayELResolver.java new file mode 100644 index 000000000..875ba1730 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ArrayELResolver.java @@ -0,0 +1,385 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.lang.reflect.Array; +import java.util.List; +import java.util.Iterator; +import java.beans.FeatureDescriptor; + +/** + * Defines property resolution behavior on arrays. + * + *

This resolver handles base objects that are Java language arrays. + * It accepts any object as a property and coerces that object into an + * integer index into the array. The resulting value is the value in the array + * at that index.

+ * + *

This resolver can be constructed in read-only mode, which means that + * {@link #isReadOnly} will always return true and + * {@link #setValue} will always throw + * PropertyNotWritableException.

+ * + *

ELResolvers are combined together using + * {@link CompositeELResolver}s, to define rich semantics for evaluating + * an expression. See the javadocs for {@link ELResolver} for details.

+ * + * @see CompositeELResolver + * @see ELResolver + * @since JSP 2.1 + */ +public class ArrayELResolver extends ELResolver { + + /** + * Creates a new read/write ArrayELResolver. + */ + public ArrayELResolver() { + this.isReadOnly = false; + } + + /** + * Creates a new ArrayELResolver whose read-only status is + * determined by the given parameter. + * + * @param isReadOnly true if this resolver cannot modify + * arrays; false otherwise. + */ + public ArrayELResolver(boolean isReadOnly) { + this.isReadOnly = isReadOnly; + } + + /** + * If the base object is an array, returns the most general acceptable type + * for a value in this array. + * + *

If the base is a array, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this method + * is called, the caller should ignore the return value.

+ * + *

Assuming the base is an array, this method will always + * return base.getClass().getComponentType(), which is + * the most general type of component that can be stored at any given + * index in the array.

+ * + * @param context The context of this evaluation. + * @param base The array to analyze. Only bases that are Java language + * arrays are handled by this resolver. + * @param property The index of the element in the array to return the + * acceptable type for. Will be coerced into an integer, but + * otherwise ignored by this resolver. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the most general acceptable type; otherwise undefined. + * @throws PropertyNotFoundException if the given index is out of + * bounds for this array. + * @throws NullPointerException if context is null + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public Class getType(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base.getClass().isArray()) { + context.setPropertyResolved(true); + int index = toInteger (property); + if (index < 0 || index >= Array.getLength(base)) { + throw new PropertyNotFoundException(); + } + return base.getClass().getComponentType(); + } + return null; + } + + /** + * If the base object is a Java language array, returns the value at the + * given index. The index is specified by the property + * argument, and coerced into an integer. If the coercion could not be + * performed, an IllegalArgumentException is thrown. If the + * index is out of bounds, null is returned. + * + *

If the base is a Java language array, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this + * method is called, the caller should ignore the return value.

+ * + * @param context The context of this evaluation. + * @param base The array to analyze. Only bases that are Java language + * arrays are handled by this resolver. + * @param property The index of the value to be returned. Will be coerced + * into an integer. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the value at the given index or null + * if the index was out of bounds. Otherwise, undefined. + * @throws IllegalArgumentException if the property could not be coerced + * into an integer. + * @throws NullPointerException if context is null. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public Object getValue(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base.getClass().isArray()) { + context.setPropertyResolved(base, property); + int index = toInteger (property); + if (index >= 0 && index < Array.getLength(base)) { + return Array.get(base, index); + } + } + return null; + } + + /** + * If the base object is a Java language array, attempts to set the + * value at the given index with the given value. The index is specified + * by the property argument, and coerced into an integer. + * If the coercion could not be performed, an + * IllegalArgumentException is thrown. If the index is + * out of bounds, a PropertyNotFoundException is thrown. + * + *

If the base is a Java language array, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this method + * is called, the caller can safely assume no value was set.

+ * + *

If this resolver was constructed in read-only mode, this method will + * always throw PropertyNotWritableException.

+ * + * @param context The context of this evaluation. + * @param base The array to be modified. Only bases that are Java language + * arrays are handled by this resolver. + * @param property The index of the value to be set. Will be coerced + * into an integer. + * @param val The value to be set at the given index. + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this array. + * @throws NullPointerException if context is null. + * @throws IllegalArgumentException if the property could not be coerced + * into an integer, or if some aspect of the specified element + * prevents it from being added to this array. + * @throws PropertyNotWritableException if this resolver was constructed + * in read-only mode. + * @throws PropertyNotFoundException if the given index is out of + * bounds for this array. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public void setValue(ELContext context, + Object base, + Object property, + Object val) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base.getClass().isArray()) { + context.setPropertyResolved(base, property); + if (isReadOnly) { + throw new PropertyNotWritableException(); + } + Class type = base.getClass().getComponentType(); + if (val != null && ! type.isAssignableFrom(val.getClass())) { + throw new ClassCastException(); + } + int index = toInteger (property); + if (index < 0 || index >= Array.getLength(base)) { + throw new PropertyNotFoundException(); + } + Array.set(base, index, val); + } + } + + /** + * If the base object is a Java language array, returns whether a call to + * {@link #setValue} will always fail. + * + *

If the base is a Java language array, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this method + * is called, the caller should ignore the return value.

+ * + *

If this resolver was constructed in read-only mode, this method will + * always return true. Otherwise, it returns + * false.

+ * + * @param context The context of this evaluation. + * @param base The array to analyze. Only bases that are a Java language + * array are handled by this resolver. + * @param property The index of the element in the array to return the + * acceptable type for. Will be coerced into an integer, but + * otherwise ignored by this resolver. + * @return If the propertyResolved property of + * ELContext was set to true, then + * true if calling the setValue method + * will always fail or false if it is possible that + * such a call may succeed; otherwise undefined. + * @throws PropertyNotFoundException if the given index is out of + * bounds for this array. + * @throws NullPointerException if context is null + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public boolean isReadOnly(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base.getClass().isArray()) { + context.setPropertyResolved(true); + int index = toInteger (property); + if (index < 0 || index >= Array.getLength(base)) { + throw new PropertyNotFoundException(); + } + } + return isReadOnly; + } + + /** + * Always returns null, since there is no reason to + * iterate through set set of all integers. + * + *

The {@link #getCommonPropertyType} method returns sufficient + * information about what properties this resolver accepts.

+ * + * @param context The context of this evaluation. + * @param base The array to analyze. Only bases that are a Java language + * array are handled by this resolver. + * @return null. + */ + public Iterator getFeatureDescriptors( + ELContext context, + Object base) { + return null; + } + + /** + * If the base object is a Java language array, returns the most general + * type that this resolver accepts for the property argument. + * Otherwise, returns null. + * + *

Assuming the base is an array, this method will always return + * Integer.class. This is because arrays accept integers + * for their index.

+ * + * @param context The context of this evaluation. + * @param base The array to analyze. Only bases that are a Java language + * array are handled by this resolver. + * @return null if base is not a Java language array; + * otherwise Integer.class. + */ + public Class getCommonPropertyType(ELContext context, + Object base) { + + if (base != null && base.getClass().isArray()) { + return Integer.class; + } + return null; + } + + private int toInteger(Object p) { + + if (p instanceof Integer) { + return ((Integer) p).intValue(); + } + if (p instanceof Character) { + return ((Character) p).charValue(); + } + if (p instanceof Boolean) { + return ((Boolean) p).booleanValue()? 1: 0; + } + if (p instanceof Number) { + return ((Number) p).intValue(); + } + if (p instanceof String) { + return Integer.parseInt((String) p); + } + throw new IllegalArgumentException(); + } + + private boolean isReadOnly; +} + diff --git a/fine-third-default/fine-javax-el/src/javax/el/BeanELResolver.java b/fine-third-default/fine-javax-el/src/javax/el/BeanELResolver.java new file mode 100644 index 000000000..6126b75bc --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/BeanELResolver.java @@ -0,0 +1,739 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.ref.SoftReference; +import java.lang.ref.ReferenceQueue; +import java.beans.FeatureDescriptor; +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.beans.IntrospectionException; +import java.util.Iterator; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Defines property resolution behavior on objects using the JavaBeans + * component architecture. + * + *

This resolver handles base objects of any type, as long as the + * base is not null. It accepts any object as a property + * or method, and coerces it to a string. + * + *

For property resolution, the + * property string is used to find a JavaBeans compliant property on + * the base object. The value is accessed using JavaBeans getters and setters. + *

+ * + *

For method resolution, the method string is the name + * of the method in the bean. The parameter types can be optionally + * specified to identify the method. If the parameter types are not + * specified, the parameter objects are used in the method resolution. + *

+ * + *

This resolver can be constructed in read-only mode, which means that + * {@link #isReadOnly} will always return true and + * {@link #setValue} will always throw + * PropertyNotWritableException.

+ * + *

ELResolvers are combined together using + * {@link CompositeELResolver}s, to define rich semantics for evaluating + * an expression. See the javadocs for {@link ELResolver} for details.

+ * + *

Because this resolver handles base objects of any type, it should + * be placed near the end of a composite resolver. Otherwise, it will + * claim to have resolved a property before any resolvers that come after + * it get a chance to test if they can do so as well.

+ * + * @see CompositeELResolver + * @see ELResolver + * @since JSP 2.1 + */ +public class BeanELResolver extends ELResolver { + + static private class BPSoftReference extends SoftReference { + final Class key; + BPSoftReference(Class key, BeanProperties beanProperties, + ReferenceQueue refQ) { + super(beanProperties, refQ); + this.key = key; + } + } + + static private class SoftConcurrentHashMap extends + ConcurrentHashMap, BeanProperties> { + + private static final int CACHE_INIT_SIZE = 1024; + private ConcurrentHashMap, BPSoftReference> map = + new ConcurrentHashMap, BPSoftReference>(CACHE_INIT_SIZE); + private ReferenceQueue refQ = + new ReferenceQueue(); + + // Remove map entries that have been placed on the queue by GC. + private void cleanup() { + BPSoftReference BPRef = null; + while ((BPRef = (BPSoftReference)refQ.poll()) != null) { + map.remove(BPRef.key); + } + } + + @Override + public BeanProperties put(Class key, BeanProperties value) { + cleanup(); + BPSoftReference prev = + map.put(key, new BPSoftReference(key, value, refQ)); + return prev == null? null: prev.get(); + } + + @Override + public BeanProperties putIfAbsent(Class key, BeanProperties value) { + cleanup(); + BPSoftReference prev = + map.putIfAbsent(key, new BPSoftReference(key, value, refQ)); + return prev == null? null: prev.get(); + } + + @Override + public BeanProperties get(Object key) { + cleanup(); + BPSoftReference BPRef = map.get(key); + if (BPRef == null) { + return null; + } + if (BPRef.get() == null) { + // value has been garbage collected, remove entry in map + map.remove(key); + return null; + } + return BPRef.get(); + } + } + + private boolean isReadOnly; + + private static final SoftConcurrentHashMap properties = + new SoftConcurrentHashMap(); + + /* + * Defines a property for a bean. + */ + final static class BeanProperty { + + private Method readMethod; + private Method writeMethod; + private PropertyDescriptor descriptor; + + public BeanProperty(Class baseClass, + PropertyDescriptor descriptor) { + this.descriptor = descriptor; + readMethod = getMethod(baseClass, descriptor.getReadMethod()); + writeMethod = getMethod(baseClass, descriptor.getWriteMethod()); + } + + public Class getPropertyType() { + return descriptor.getPropertyType(); + } + + public boolean isReadOnly() { + return getWriteMethod() == null; + } + + public Method getReadMethod() { + return readMethod; + } + + public Method getWriteMethod() { + return writeMethod; + } + } + + /* + * Defines the properties for a bean. + */ + final static class BeanProperties { + + private final Map propertyMap = + new HashMap(); + + public BeanProperties(Class baseClass) { + PropertyDescriptor[] descriptors; + try { + BeanInfo info = Introspector.getBeanInfo(baseClass); + descriptors = info.getPropertyDescriptors(); + } catch (IntrospectionException ie) { + throw new ELException(ie); + } + for (PropertyDescriptor pd: descriptors) { + propertyMap.put(pd.getName(), + new BeanProperty(baseClass, pd)); + } + } + + public BeanProperty getBeanProperty(String property) { + return propertyMap.get(property); + } + } + + /** + * Creates a new read/write BeanELResolver. + */ + public BeanELResolver() { + this.isReadOnly = false; + } + + /** + * Creates a new BeanELResolver whose read-only status is + * determined by the given parameter. + * + * @param isReadOnly true if this resolver cannot modify + * beans; false otherwise. + */ + public BeanELResolver(boolean isReadOnly) { + this.isReadOnly = isReadOnly; + } + + /** + * If the base object is not null, returns the most + * general acceptable type that can be set on this bean property. + * + *

If the base is not null, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this + * method is called, the caller should ignore the return value.

+ * + *

The provided property will first be coerced to a String. + * If there is a BeanInfoProperty for this property and + * there were no errors retrieving it, the propertyType of + * the propertyDescriptor is returned. Otherwise, a + * PropertyNotFoundException is thrown.

+ * + * @param context The context of this evaluation. + * @param base The bean to analyze. + * @param property The name of the property to analyze. Will be coerced to + * a String. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the most general acceptable type; otherwise undefined. + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if base is not + * null and the specified property does not exist + * or is not readable. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public Class getType(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base == null || property == null){ + return null; + } + + BeanProperty bp = getBeanProperty(context, base, property); + context.setPropertyResolved(true); + return bp.getPropertyType(); + } + + /** + * If the base object is not null, returns the current + * value of the given property on this bean. + * + *

If the base is not null, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this + * method is called, the caller should ignore the return value.

+ * + *

The provided property name will first be coerced to a + * String. If the property is a readable property of the + * base object, as per the JavaBeans specification, then return the + * result of the getter call. If the getter throws an exception, + * it is propagated to the caller. If the property is not found or is + * not readable, a PropertyNotFoundException is thrown.

+ * + * @param context The context of this evaluation. + * @param base The bean on which to get the property. + * @param property The name of the property to get. Will be coerced to + * a String. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the value of the given property. Otherwise, undefined. + * @throws NullPointerException if context is null. + * @throws PropertyNotFoundException if base is not + * null and the specified property does not exist + * or is not readable. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public Object getValue(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base == null || property == null){ + return null; + } + + BeanProperty bp = getBeanProperty(context, base, property); + Method method = bp.getReadMethod(); + if (method == null) { + throw new PropertyNotFoundException( + ELUtil.getExceptionMessageString(context, + "propertyNotReadable", + new Object[] { base.getClass().getName(), + property.toString()})); + } + + Object value; + try { + value = method.invoke(base, new Object[0]); + context.setPropertyResolved(base, property); + } catch (ELException ex) { + throw ex; + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } catch (Exception ex) { + throw new ELException(ex); + } + return value; + } + + /** + * If the base object is not null, attempts to set the + * value of the given property on this bean. + * + *

If the base is not null, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this + * method is called, the caller can safely assume no value was set.

+ * + *

If this resolver was constructed in read-only mode, this method will + * always throw PropertyNotWritableException.

+ * + *

The provided property name will first be coerced to a + * String. If property is a writable property of + * base (as per the JavaBeans Specification), the setter + * method is called (passing value). If the property exists + * but does not have a setter, then a + * PropertyNotFoundException is thrown. If the property + * does not exist, a PropertyNotFoundException is thrown.

+ * + * @param context The context of this evaluation. + * @param base The bean on which to set the property. + * @param property The name of the property to set. Will be coerced to + * a String. + * @param val The value to be associated with the specified key. + * @throws NullPointerException if context is null. + * @throws PropertyNotFoundException if base is not + * null and the specified property does not exist. + * @throws PropertyNotWritableException if this resolver was constructed + * in read-only mode, or if there is no setter for the property. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public void setValue(ELContext context, + Object base, + Object property, + Object val) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base == null || property == null){ + return; + } + + if (isReadOnly) { + throw new PropertyNotWritableException( + ELUtil.getExceptionMessageString(context, + "resolverNotwritable", + new Object[] { base.getClass().getName() })); + } + + BeanProperty bp = getBeanProperty(context, base, property); + Method method = bp.getWriteMethod(); + if (method == null) { + throw new PropertyNotWritableException( + ELUtil.getExceptionMessageString(context, + "propertyNotWritable", + new Object[] { base.getClass().getName(), + property.toString()})); + } + + try { + method.invoke(base, new Object[] {val}); + context.setPropertyResolved(base, property); + } catch (ELException ex) { + throw ex; + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } catch (Exception ex) { + if (null == val) { + val = "null"; + } + String message = ELUtil.getExceptionMessageString(context, + "setPropertyFailed", + new Object[] { property.toString(), + base.getClass().getName(), val }); + throw new ELException(message, ex); + } + } + + /** + * If the base object is not null, invoke the method, with + * the given parameters on this bean. The return value from the method + * is returned. + * + *

If the base is not null, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this + * method is called, the caller should ignore the return value.

+ * + *

The provided method object will first be coerced to a + * String. The methods in the bean is then examined and + * an attempt will be made to select one for invocation. If no suitable + * can be found, a MethodNotFoundException is thrown. + * + * If the given paramTypes is not null, select the method + * with the given name and parameter types. + * + * Else select the method with the given name that has the same number + * of parameters. If there are more than one such method, the method + * selection process is undefined. + * + * Else select the method with the given name that takes a variable + * number of arguments. + * + * Note the resolution for overloaded methods will likely be clarified + * in a future version of the spec. + * + * The provide parameters are coerced to the corresponding parameter + * types of the method, and the method is then invoked. + * + * @param context The context of this evaluation. + * @param base The bean on which to invoke the method + * @param method The simple name of the method to invoke. + * Will be coerced to a String. If method is + * "<init>"or "<clinit>" a MethodNotFoundException is + * thrown. + * @param paramTypes An array of Class objects identifying the + * method's formal parameter types, in declared order. + * Use an empty array if the method has no parameters. + * Can be null, in which case the method's formal + * parameter types are assumed to be unknown. + * @param params The parameters to pass to the method, or + * null if no parameters. + * @return The result of the method invocation (null if + * the method has a void return type). + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if an exception was thrown while performing + * (base, method) resolution. The thrown exception must be + * included as the cause property of this exception, if + * available. If the exception thrown is an + * InvocationTargetException, extract its + * cause and pass it to the + * ELException constructor. + * @since EL 2.2 + */ + + public Object invoke(ELContext context, + Object base, + Object method, + Class[] paramTypes, + Object[] params) { + + if (base == null || method == null) { + return null; + } + Method m = ELUtil.findMethod(base.getClass(), method.toString(), + paramTypes,params, false); + for (Object p: params) { + // If the parameters is a LambdaExpression, set the ELContext + // for its evaluation + if (p instanceof javax.el.LambdaExpression) { + ((javax.el.LambdaExpression) p).setELContext(context); + } + } + Object ret = ELUtil.invokeMethod(context, m, base, params); + context.setPropertyResolved(base, method); + return ret; + } + + /** + * If the base object is not null, returns whether a call + * to {@link #setValue} will always fail. + * + *

If the base is not null, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this + * method is called, the caller can safely assume no value was set.

+ * + *

If this resolver was constructed in read-only mode, this method will + * always return true.

+ * + *

The provided property name will first be coerced to a + * String. If property is a writable property of + * base, false is returned. If the property is + * found but is not writable, true is returned. If the + * property is not found, a PropertyNotFoundException + * is thrown.

+ * + * @param context The context of this evaluation. + * @param base The bean to analyze. + * @param property The name of the property to analyzed. Will be coerced to + * a String. + * @return If the propertyResolved property of + * ELContext was set to true, then + * true if calling the setValue method + * will always fail or false if it is possible that + * such a call may succeed; otherwise undefined. + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if base is not + * null and the specified property does not exist. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public boolean isReadOnly(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base == null || property == null){ + return false; + } + + context.setPropertyResolved(true); + if (isReadOnly) { + return true; + } + + BeanProperty bp = getBeanProperty(context, base, property); + return bp.isReadOnly(); + } + + /** + * If the base object is not null, returns an + * Iterator containing the set of JavaBeans properties + * available on the given object. Otherwise, returns null. + * + *

The Iterator returned must contain zero or more + * instances of {@link java.beans.FeatureDescriptor}. Each info object + * contains information about a property in the bean, as obtained by + * calling the BeanInfo.getPropertyDescriptors method. + * The FeatureDescriptor is initialized using the same + * fields as are present in the PropertyDescriptor, + * with the additional required named attributes "type" and + * "resolvableAtDesignTime" set as follows: + *

+ *
  • {@link ELResolver#TYPE} - The runtime type of the property, from + * PropertyDescriptor.getPropertyType().
  • + *
  • {@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - true.
  • + *
    + *

    + * + * @param context The context of this evaluation. + * @param base The bean to analyze. + * @return An Iterator containing zero or more + * FeatureDescriptor objects, each representing a property + * on this bean, or null if the base + * object is null. + */ + public Iterator getFeatureDescriptors( + ELContext context, + Object base) { + if (base == null){ + return null; + } + + BeanInfo info = null; + try { + info = Introspector.getBeanInfo(base.getClass()); + } catch (Exception ex) { + } + if (info == null) { + return null; + } + ArrayList list = new ArrayList( + info.getPropertyDescriptors().length); + for (PropertyDescriptor pd: info.getPropertyDescriptors()) { + pd.setValue("type", pd.getPropertyType()); + pd.setValue("resolvableAtDesignTime", Boolean.TRUE); + list.add(pd); + } + return list.iterator(); + } + + /** + * If the base object is not null, returns the most + * general type that this resolver accepts for the + * property argument. Otherwise, returns null. + * + *

    Assuming the base is not null, this method will always + * return Object.class. This is because any object is + * accepted as a key and is coerced into a string.

    + * + * @param context The context of this evaluation. + * @param base The bean to analyze. + * @return null if base is null; otherwise + * Object.class. + */ + public Class getCommonPropertyType(ELContext context, + Object base) { + if (base == null){ + return null; + } + + return Object.class; + } + + /* + * Get a public method form a public class or interface of a given method. + * Note that if a PropertyDescriptor is obtained for a non-public class that + * implements a public interface, the read/write methods will be for the + * class, and therefore inaccessible. To correct this, a version of the + * same method must be found in a superclass or interface. + **/ + + static private Method getMethod(Class cl, Method method) { + + if (method == null) { + return null; + } + + if (Modifier.isPublic (cl.getModifiers ())) { + return method; + } + Class [] interfaces = cl.getInterfaces (); + for (int i = 0; i < interfaces.length; i++) { + Class c = interfaces[i]; + Method m = null; + try { + m = c.getMethod(method.getName(), method.getParameterTypes()); + c = m.getDeclaringClass(); + if ((m = getMethod(c, m)) != null) + return m; + } catch (NoSuchMethodException ex) { + } + } + Class c = cl.getSuperclass(); + if (c != null) { + Method m = null; + try { + m = c.getMethod(method.getName(), method.getParameterTypes()); + c = m.getDeclaringClass(); + if ((m = getMethod(c, m)) != null) + return m; + } catch (NoSuchMethodException ex) { + } + } + return null; + } + + private BeanProperty getBeanProperty(ELContext context, + Object base, + Object prop) { + + String property = prop.toString(); + Class baseClass = base.getClass(); + BeanProperties bps = properties.get(baseClass); + if (bps == null) { + bps = new BeanProperties(baseClass); + properties.put(baseClass, bps); + } + BeanProperty bp = bps.getBeanProperty(property); + if (bp == null) { + throw new PropertyNotFoundException( + ELUtil.getExceptionMessageString(context, + "propertyNotFound", + new Object[] { baseClass.getName(), + property})); + } + return bp; + } +} + diff --git a/fine-third-default/fine-javax-el/src/javax/el/BeanNameELResolver.java b/fine-third-default/fine-javax-el/src/javax/el/BeanNameELResolver.java new file mode 100644 index 000000000..a242fc2aa --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/BeanNameELResolver.java @@ -0,0 +1,256 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package javax.el; + +import java.util.Iterator; +import java.beans.FeatureDescriptor; + +/** + *

    An ELResolver for resolving user or container managed beans.

    + *

    A {@link BeanNameResolver} is required for its proper operation. + * The following example creates an ELResolver that + * resolves the name "bean" to an instance of MyBean. + *

    + *
    + * ELResovler elr = new BeanNameELResolver(new BeanNameResolver {
    + *    public boolean isNameResolved(String beanName) {
    + *       return "bean".equals(beanName);
    + *    }
    + *    public Object getBean(String beanName) {
    + *       return "bean".equals(beanName)? new MyBean(): null;
    + *    }
    + * });
    + * 
    + *
    + *

    + * @since EL 3.0 + */ +public class BeanNameELResolver extends ELResolver { + + private BeanNameResolver beanNameResolver; + + /** + * Constructor + * @param beanNameResolver The {@link BeanNameResolver} that resolves a bean name. + */ + public BeanNameELResolver(BeanNameResolver beanNameResolver) { + this.beanNameResolver = beanNameResolver; + } + + /** + * If the base object is null and the property is a name + * that is resolvable by the BeanNameResolver, returns the value + * resolved by the BeanNameResolver. + * + *

    If name is resolved by the BeanNameResolver, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this + * method is called, the caller should ignore the return value.

    + * + * @param context The context of this evaluation. + * @param base null + * @param property The name of the bean. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the value of the bean with the given name. Otherwise, undefined. + * @throws NullPointerException if context is null. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + if (base == null && property instanceof String) { + if (beanNameResolver.isNameResolved((String) property)) { + context.setPropertyResolved(base, property); + return beanNameResolver.getBean((String) property); + } + } + return null; + } + + /** + * If the base is null and the property is a name that is resolvable by + * the BeanNameResolver, the bean in the BeanNameResolver is set to the + * given value. + * + *

    If the name is resolvable by the BeanNameResolver, or if the + * BeanNameResolver allows creating a new bean, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller can + * safely assume no value has been set.

    + * + * @param context The context of this evaluation. + * @param base null + * @param property The name of the bean + * @param value The value to set the bean with the given name to. + * @throws NullPointerException if context is null + * @throws PropertyNotWritableException if the BeanNameResolver does not + * allow the bean to be modified. + * @throws ELException if an exception was thrown while attempting to + * set the bean with the given name. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + @Override + public void setValue(ELContext context, Object base, Object property, + Object value) { + if (context == null) { + throw new NullPointerException(); + } + + if (base == null && property instanceof String) { + String beanName = (String) property; + if (beanNameResolver.isNameResolved(beanName) || + beanNameResolver.canCreateBean(beanName)) { + beanNameResolver.setBeanValue(beanName, value); + context.setPropertyResolved(base, property); + } + } + } + + /** + * If the base is null and the property is a name resolvable by + * the BeanNameResolver, return the type of the bean. + * + *

    If the name is resolvable by the BeanNameResolver, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller can + * safely assume no value has been set.

    + * + * @param context The context of this evaluation. + * @param base null + * @param property The name of the bean. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the type of the bean with the given name. Otherwise, undefined. + * @throws NullPointerException if context is null. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + @Override + public Class getType(ELContext context, Object base, Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base == null && property instanceof String) { + if (beanNameResolver.isNameResolved((String) property)) { + context.setPropertyResolved(true); + return beanNameResolver.getBean((String) property).getClass(); + } + } + return null; + } + + /** + * If the base is null and the property is a name resolvable by + * the BeanNameResolver, attempts to determine if the bean is writable. + * + *

    If the name is resolvable by the BeanNameResolver, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller can + * safely assume no value has been set.

    + * + * @param context The context of this evaluation. + * @param base null + * @param property The name of the bean. + * @return If the propertyResolved property of + * ELContext was set to true, then + * true if the property is read-only or + * false if not; otherwise undefined. + * @throws NullPointerException if context is null. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base == null && property instanceof String) { + if (beanNameResolver.isNameResolved((String) property)) { + context.setPropertyResolved(true); + return beanNameResolver.isReadOnly((String) property); + } + } + return false; + } + + /** + * Always returns null, since there is no reason to + * iterate through a list of one element: bean name. + * @param context The context of this evaluation. + * @param base null. + * @return null. + */ + public Iterator getFeatureDescriptors( + ELContext context, Object base) { + return null; + } + + /** + * Always returns String.class, since a bean name is a String. + * @param context The context of this evaluation. + * @param base null. + * @return String.class. + */ + @Override + public Class getCommonPropertyType(ELContext context, Object base) { + return String.class; + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/BeanNameResolver.java b/fine-third-default/fine-javax-el/src/javax/el/BeanNameResolver.java new file mode 100644 index 000000000..81075d7f2 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/BeanNameResolver.java @@ -0,0 +1,107 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.el; + +/** + * Resolves a bean by its known name. + * This class can be extended to return a bean object given its name, + * to set a value to an existing bean, or to create a bean with the value. + * @see BeanNameELResolver + * + * @since EL 3.0 + */ +public abstract class BeanNameResolver { + /** + * Returns whether the given name is resolved by the BeanNameResolver + * + * @param beanName The name of the bean. + * @return true if the name is resolved by this BeanNameResolver; false + * otherwise. + */ + public boolean isNameResolved(String beanName) { + return false; + } + + /** + * Returns the bean known by its name. + * @param beanName The name of the bean. + * @return The bean with the given name. Can be null. + * + */ + public Object getBean(String beanName) { + return null; + } + + /** + * Sets a value to a bean of the given name. + * If the bean of the given name + * does not exist and if {@link #canCreateBean} is true, + * one is created with the given value. + * @param beanName The name of the bean + * @param value The value to set the bean to. Can be null. + * @throws PropertyNotWritableException if the bean cannot be + * modified or created. + */ + public void setBeanValue(String beanName, Object value) + throws PropertyNotWritableException { + throw new PropertyNotWritableException(); + } + + /** + * Indicates if the bean of the given name is read-only or writable + * @param beanName The name of the bean + * @return true if the bean can be set to a new value. + * false otherwise. + */ + public boolean isReadOnly(String beanName) { + return true; + } + + /** + * Allow creating a bean of the given name if it does not exist. + * @param beanName The name of the bean + * @return true if bean creation is supported + * false otherwise. + */ + public boolean canCreateBean(String beanName) { + return false; + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/CompositeELResolver.java b/fine-third-default/fine-javax-el/src/javax/el/CompositeELResolver.java new file mode 100644 index 000000000..9db25c4a9 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/CompositeELResolver.java @@ -0,0 +1,633 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.util.ArrayList; +import java.util.Iterator; +import java.beans.FeatureDescriptor; + +/** + * Maintains an ordered composite list of child ELResolvers. + * + *

    Though only a single ELResolver is associated with an + * ELContext, there are usually multiple resolvers considered + * for any given variable or property resolution. ELResolvers + * are combined together using a CompositeELResolver, to define + * rich semantics for evaluating an expression.

    + * + *

    For the {@link #getValue}, {@link #getType}, {@link #setValue} and + * {@link #isReadOnly} methods, an ELResolver is not + * responsible for resolving all possible (base, property) pairs. In fact, + * most resolvers will only handle a base of a single type. + * To indicate that a resolver has successfully resolved a particular + * (base, property) pair, it must set the propertyResolved + * property of the ELContext to true. If it could + * not handle the given pair, it must leave this property alone. The caller + * must ignore the return value of the method if propertyResolved + * is false.

    + * + *

    The CompositeELResolver initializes the + * ELContext.propertyResolved flag to false, and uses + * it as a stop condition for iterating through its component resolvers.

    + * + *

    The ELContext.propertyResolved flag is not used for the + * design-time methods {@link #getFeatureDescriptors} and + * {@link #getCommonPropertyType}. Instead, results are collected and + * combined from all child ELResolvers for these methods.

    + * + * @see ELContext + * @see ELResolver + * @since JSP 2.1 + */ +public class CompositeELResolver extends ELResolver { + + public CompositeELResolver() { + this.size = 0; + this.elResolvers = new ELResolver[16]; + } + + /** + * Adds the given resolver to the list of component resolvers. + * + *

    Resolvers are consulted in the order in which they are added.

    + * + * @param elResolver The component resolver to add. + * @throws NullPointerException If the provided resolver is + * null. + */ + public void add(ELResolver elResolver) { + + if (elResolver == null) { + throw new NullPointerException(); + } + + if (size >= elResolvers.length) { + ELResolver[] newResolvers = new ELResolver[size * 2]; + System.arraycopy(elResolvers, 0, newResolvers, 0, size); + elResolvers = newResolvers; + } + + elResolvers[size++] = elResolver; + } + + /** + * Attempts to resolve the given property object on the given + * base object by querying all component resolvers. + * + *

    If this resolver handles the given (base, property) pair, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller should ignore + * the return value.

    + * + *

    First, propertyResolved is set to false on + * the provided ELContext.

    + * + *

    Next, for each component resolver in this composite: + *

      + *
    1. The getValue() method is called, passing in + * the provided context, base and + * property.
    2. + *
    3. If the ELContext's propertyResolved + * flag is false then iteration continues.
    4. + *
    5. Otherwise, iteration stops and no more component resolvers are + * considered. The value returned by getValue() is + * returned by this method.
    6. + *

    + * + *

    If none of the component resolvers were able to perform this + * operation, the value null is returned and the + * propertyResolved flag remains set to + * false

    . + * + *

    Any exception thrown by component resolvers during the iteration + * is propagated to the caller of this method.

    + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be returned, + * or null to resolve a top-level variable. + * @param property The property or variable to be resolved. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the result of the variable or property resolution; otherwise + * undefined. + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if the given (base, property) pair + * is handled by this ELResolver but the specified + * variable or property does not exist or is not readable. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public Object getValue(ELContext context, + Object base, + Object property) { + + context.setPropertyResolved(false); + + Object value = null; + for (int i = 0; i < size; i++) { + value = elResolvers[i].getValue(context, base, property); + if (context.isPropertyResolved()) { + return value; + } + } + return null; + } + + /** + * Attemps to resolve and invoke the given method on the given + * base object by querying all component resolvers. + * + *

    If this resolver handles the given (base, method) pair, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller should ignore + * the return value.

    + * + *

    First, propertyResolved is set to false on + * the provided ELContext.

    + * + *

    Next, for each component resolver in this composite: + *

      + *
    1. The invoke() method is called, passing in + * the provided context, base, + * method, paramTypes, and + * params.
    2. + *
    3. If the ELContext's propertyResolved + * flag is false then iteration continues.
    4. + *
    5. Otherwise, iteration stops and no more component resolvers are + * considered. The value returned by getValue() is + * returned by this method.
    6. + *

    + * + *

    If none of the component resolvers were able to perform this + * operation, the value null is returned and the + * propertyResolved flag remains set to + * false

    . + * + *

    Any exception thrown by component resolvers during the iteration + * is propagated to the caller of this method.

    + * + * @param context The context of this evaluation. + * @param base The bean on which to invoke the method + * @param method The simple name of the method to invoke. + * Will be coerced to a String. + * @param paramTypes An array of Class objects identifying the + * method's formal parameter types, in declared order. + * Use an empty array if the method has no parameters. + * Can be null, in which case the method's formal + * parameter types are assumed to be unknown. + * @param params The parameters to pass to the method, or + * null if no parameters. + * @return The result of the method invocation (null if + * the method has a void return type). + * @since EL 2.2 + */ + public Object invoke(ELContext context, + Object base, + Object method, + Class[] paramTypes, + Object[] params) { + + context.setPropertyResolved(false); + + Object value; + for (int i = 0; i < size; i++) { + value = elResolvers[i].invoke(context, base, method, + paramTypes, params); + if (context.isPropertyResolved()) { + return value; + } + } + return null; + } + + /** + * For a given base and property, attempts to + * identify the most general type that is acceptable for an object to be + * passed as the value parameter in a future call + * to the {@link #setValue} method. The result is obtained by + * querying all component resolvers. + * + *

    If this resolver handles the given (base, property) pair, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller should ignore + * the return value.

    + * + *

    First, propertyResolved is set to false on + * the provided ELContext.

    + * + *

    Next, for each component resolver in this composite: + *

      + *
    1. The getType() method is called, passing in + * the provided context, base and + * property.
    2. + *
    3. If the ELContext's propertyResolved + * flag is false then iteration continues.
    4. + *
    5. Otherwise, iteration stops and no more component resolvers are + * considered. The value returned by getType() is + * returned by this method.
    6. + *

    + * + *

    If none of the component resolvers were able to perform this + * operation, the value null is returned and the + * propertyResolved flag remains set to + * false

    . + * + *

    Any exception thrown by component resolvers during the iteration + * is propagated to the caller of this method.

    + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be analyzed, + * or null to analyze a top-level variable. + * @param property The property or variable to return the acceptable + * type for. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the most general acceptable type; otherwise undefined. + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if the given (base, property) pair + * is handled by this ELResolver but the specified + * variable or property does not exist or is not readable. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public Class getType(ELContext context, + Object base, + Object property) { + + context.setPropertyResolved(false); + + Class type; + for (int i = 0; i < size; i++) { + type = elResolvers[i].getType(context, base, property); + if (context.isPropertyResolved()) { + return type; + } + } + return null; + } + + /** + * Attempts to set the value of the given property + * object on the given base object. All component + * resolvers are asked to attempt to set the value. + * + *

    If this resolver handles the given (base, property) pair, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller can + * safely assume no value has been set.

    + * + *

    First, propertyResolved is set to false on + * the provided ELContext.

    + * + *

    Next, for each component resolver in this composite: + *

      + *
    1. The setValue() method is called, passing in + * the provided context, base, + * property and value.
    2. + *
    3. If the ELContext's propertyResolved + * flag is false then iteration continues.
    4. + *
    5. Otherwise, iteration stops and no more component resolvers are + * considered.
    6. + *

    + * + *

    If none of the component resolvers were able to perform this + * operation, the propertyResolved flag remains set to + * false

    . + * + *

    Any exception thrown by component resolvers during the iteration + * is propagated to the caller of this method.

    + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be set, + * or null to set a top-level variable. + * @param property The property or variable to be set. + * @param val The value to set the property or variable to. + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if the given (base, property) pair + * is handled by this ELResolver but the specified + * variable or property does not exist. + * @throws PropertyNotWritableException if the given (base, property) + * pair is handled by this ELResolver but the specified + * variable or property is not writable. + * @throws ELException if an exception was thrown while attempting to + * set the property or variable. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public void setValue(ELContext context, + Object base, + Object property, + Object val) { + + context.setPropertyResolved(false); + + for (int i = 0; i < size; i++) { + elResolvers[i].setValue(context, base, property, val); + if (context.isPropertyResolved()) { + return; + } + } + } + + /** + * For a given base and property, attempts to + * determine whether a call to {@link #setValue} will always fail. The + * result is obtained by querying all component resolvers. + * + *

    If this resolver handles the given (base, property) pair, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller should ignore + * the return value.

    + * + *

    First, propertyResolved is set to false on + * the provided ELContext.

    + * + *

    Next, for each component resolver in this composite: + *

      + *
    1. The isReadOnly() method is called, passing in + * the provided context, base and + * property.
    2. + *
    3. If the ELContext's propertyResolved + * flag is false then iteration continues.
    4. + *
    5. Otherwise, iteration stops and no more component resolvers are + * considered. The value returned by isReadOnly() is + * returned by this method.
    6. + *

    + * + *

    If none of the component resolvers were able to perform this + * operation, the value false is returned and the + * propertyResolved flag remains set to + * false

    . + * + *

    Any exception thrown by component resolvers during the iteration + * is propagated to the caller of this method.

    + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be analyzed, + * or null to analyze a top-level variable. + * @param property The property or variable to return the read-only status + * for. + * @return If the propertyResolved property of + * ELContext was set to true, then + * true if the property is read-only or + * false if not; otherwise undefined. + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if the given (base, property) pair + * is handled by this ELResolver but the specified + * variable or property does not exist. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public boolean isReadOnly(ELContext context, + Object base, + Object property) { + + context.setPropertyResolved(false); + + boolean readOnly; + for (int i = 0; i < size; i++) { + readOnly = elResolvers[i].isReadOnly(context, base, property); + if (context.isPropertyResolved()) { + return readOnly; + } + } + return false; // Does not matter + } + + /** + * Returns information about the set of variables or properties that + * can be resolved for the given base object. One use for + * this method is to assist tools in auto-completion. The results are + * collected from all component resolvers. + * + *

    The propertyResolved property of the + * ELContext is not relevant to this method. + * The results of all ELResolvers are concatenated.

    + * + *

    The Iterator returned is an iterator over the + * collection of FeatureDescriptor objects returned by + * the iterators returned by each component resolver's + * getFeatureDescriptors method. If null is + * returned by a resolver, it is skipped.

    + * + * @param context The context of this evaluation. + * @param base The base object whose set of valid properties is to + * be enumerated, or null to enumerate the set of + * top-level variables that this resolver can evaluate. + * @return An Iterator containing zero or more (possibly + * infinitely more) FeatureDescriptor objects, or + * null if this resolver does not handle the given + * base object or that the results are too complex to + * represent with this method + */ + public Iterator getFeatureDescriptors( + ELContext context, + Object base) { + return new CompositeIterator(elResolvers, size, context, base); + } + + /** + * Returns the most general type that this resolver accepts for the + * property argument, given a base object. + * One use for this method is to assist tools in auto-completion. The + * result is obtained by querying all component resolvers. + * + *

    The Class returned is the most specific class that is + * a common superclass of all the classes returned by each component + * resolver's getCommonPropertyType method. If + * null is returned by a resolver, it is skipped.

    + * + * @param context The context of this evaluation. + * @param base The base object to return the most general property + * type for, or null to enumerate the set of + * top-level variables that this resolver can evaluate. + * @return null if this ELResolver does not + * know how to handle the given base object; otherwise + * Object.class if any type of property + * is accepted; otherwise the most general property + * type accepted for the given base. + */ + public Class getCommonPropertyType(ELContext context, + Object base) { + Class commonPropertyType = null; + for (int i = 0; i < size; i++) { + + Class type = elResolvers[i].getCommonPropertyType(context, base); + if (type == null) { + // skip this EL Resolver + continue; + } else if (commonPropertyType == null) { + commonPropertyType = type; + } else if (commonPropertyType.isAssignableFrom(type)) { + continue; + } else if (type.isAssignableFrom(commonPropertyType)) { + commonPropertyType = type; + } else { + // Don't have a commonPropertyType + return null; + } + } + return commonPropertyType; + } + + /** + * Converts an object to a specific type. + * + *

    An ELException is thrown if an error occurs during + * the conversion.

    + * + * @param context The context of this evaluation. + * @param obj The object to convert. + * @param targetType The target type for the convertion. + * @throws ELException thrown if errors occur. + * + * @since EL 3.0 + */ + @Override + public Object convertToType(ELContext context, + Object obj, + Class targetType) { + + context.setPropertyResolved(false); + + Object value = null; + for (int i = 0; i < size; i++) { + value = elResolvers[i].convertToType(context, obj, targetType); + if (context.isPropertyResolved()) { + return value; + } + } + return null; + } + + private ELResolver[] elResolvers; + private int size; + + private static class CompositeIterator + implements Iterator { + + ELResolver[] resolvers; + int size; + int index = 0; + Iterator propertyIter = null; + ELContext context; + Object base; + + CompositeIterator(ELResolver[] resolvers, + int size, + ELContext context, + Object base) { + this.resolvers = resolvers; + this.size = size; + this.context = context; + this.base = base; + } + + public boolean hasNext() { + if (propertyIter == null || !propertyIter.hasNext()) { + while (index < size) { + ELResolver elResolver = resolvers[index++]; + propertyIter = elResolver.getFeatureDescriptors( + context, base); + if (propertyIter != null) { + return propertyIter.hasNext(); + } + } + return false; + } + return propertyIter.hasNext(); + } + + public FeatureDescriptor next() { + if (propertyIter == null || !propertyIter.hasNext()) { + while (index < size) { + ELResolver elResolver = resolvers[index++]; + propertyIter = elResolver.getFeatureDescriptors( + context, base); + if (propertyIter != null) { + return propertyIter.next(); + } + } + return null; + } + return propertyIter.next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } +} + diff --git a/fine-third-default/fine-javax-el/src/javax/el/ELClass.java b/fine-third-default/fine-javax-el/src/javax/el/ELClass.java new file mode 100644 index 000000000..89c434e8e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ELClass.java @@ -0,0 +1,71 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package javax.el; + +/** + *

    A runtime representation of a Class in the EL expressions. + * It encapsulates the java.lang.Class instance.

    + * + *

    This class is used only in {@link StaticFieldELResolver} and will + * probably only be of interest to EL implementors, and not EL users. + * + * @since EL 3.0 + */ + +public class ELClass { + + private Class klass; + + /** + * Constructor + * @param klass The Class instance + */ + public ELClass(Class klass) { + this.klass = klass; + } + + /** + * Returns the Class instance + * @return The Class instance + */ + public Class getKlass() { + return this.klass; + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ELContext.java b/fine-third-default/fine-javax-el/src/javax/el/ELContext.java new file mode 100644 index 000000000..8aaa507c0 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ELContext.java @@ -0,0 +1,482 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.util.Map; +import java.util.Stack; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Locale; + +/** + * Context information for expression parsing and evaluation. + * + *

    To parse or evaluate an {@link Expression}, an ELContext + * must be provided. The ELContext holds: + *

      + *
    • a reference to {@link FunctionMapper} that will be used + * to resolve EL Functions. This is used only in parsing.
    • + *
    • a reference to {@link VariableMapper} that will be used + * to resolve EL Variables. This is used only in parsing.
    • + *
    • a reference to the base {@link ELResolver} that will be consulted + * to resolve model objects and their properties
    • + *
    • a collection of all the relevant context objects for use by + * ELResolvers
    • + *
    • state information during the evaluation of an expression, such as + * whether a property has been resolved yet
    • + *
    • a reference to {@link ImportHandler} that will be consulted to + * resolve classes that have been imported
    • + *
    • a reference to the arguments for the active {@link LambdaExpression}s
    • + *
    • a reference to the list of registered evaluation listeners
    • + *

    + * + *

    The collection of context objects is necessary because each + * ELResolver may need access to a different context object. + * For example, JSP and Faces resolvers need access to a + * {@link javax.servlet.jsp.JspContext} and a + * {@link javax.faces.context.FacesContext}, respectively.

    + * + *

    When used in a web container, the creation of + * ELContext objects is controlled through + * the underlying technology. For example, in JSP the + * JspContext.getELContext() factory method is used. + * Some technologies provide the ability to add an {@link ELContextListener} + * so that applications and frameworks can ensure their own context objects + * are attached to any newly created ELContext.

    + * + *

    When used in a stand-alone environment, {@link StandardELContext} + * provides a default ELContext, which is managed and modified + * by {@link ELManager}. + * + *

    Because it stores state during expression evaluation, an + * ELContext object is not thread-safe. Care should be taken + * to never share an ELContext instance between two or more + * threads.

    + * + * @see ELContextListener + * @see ELContextEvent + * @see ELResolver + * @see FunctionMapper + * @see VariableMapper + * @see ImportHandler + * @see LambdaExpression + * @see StandardELContext + * @see javax.servlet.jsp.JspContext + * @since EL 2.1 and EL 3.0 + */ +public abstract class ELContext { + + /** + * Called to indicate that a ELResolver has successfully + * resolved a given (base, property) pair. + * Use {@link #setPropertyResolved(Object, Object)} if + * resolved is true and to notify {@link EvaluationListener}s. + * + *

    The {@link CompositeELResolver} checks this property to determine + * whether it should consider or skip other component resolvers.

    + * + * @see CompositeELResolver + * @param resolved true if the property has been resolved, or false if + * not. + */ + public void setPropertyResolved(boolean resolved) { + this.resolved = resolved; + } + + /** + * Called to indicate that a ELResolver has successfully + * resolved a given (base, property) pair and to notify the + * {@link EvaluationListener}s. + * + *

    The {@link CompositeELResolver} checks this property to determine + * whether it should consider or skip other component resolvers.

    + * + * @see CompositeELResolver + * @param base The base object + * @param property The property object + * + * @since EL 3.0 + */ + public void setPropertyResolved(Object base, Object property) { + setPropertyResolved(true); // Don't set the variable here, for 2.2 users + // ELContext may be overridden or delegated. + notifyPropertyResolved(base, property); + } + + /** + * Returns whether an {@link ELResolver} has successfully resolved a + * given (base, property) pair. + * + *

    The {@link CompositeELResolver} checks this property to determine + * whether it should consider or skip other component resolvers.

    + * + * @see CompositeELResolver + * @return true if the property has been resolved, or false if not. + */ + public boolean isPropertyResolved() { + return resolved; + } + + /** + * Associates a context object with this ELContext. + * + *

    The ELContext maintains a collection of context objects + * relevant to the evaluation of an expression. These context objects + * are used by ELResolvers. This method is used to + * add a context object to that collection.

    + * + *

    By convention, the contextObject will be of the + * type specified by the key. However, this is not + * required and the key is used strictly as a unique identifier.

    + * + * @param key The key used by an @{link ELResolver} to identify this + * context object. + * @param contextObject The context object to add to the collection. + * @throws NullPointerException if key is null or contextObject is null. + */ + public void putContext(Class key, Object contextObject) { + if((key == null) || (contextObject == null)) { + throw new NullPointerException(); + } + map.put(key, contextObject); + } + + /** + * Returns the context object associated with the given key. + * + *

    The ELContext maintains a collection of context objects + * relevant to the evaluation of an expression. These context objects + * are used by ELResolvers. This method is used to + * retrieve the context with the given key from the collection.

    + * + *

    By convention, the object returned will be of the type specified by + * the key. However, this is not required and the key is + * used strictly as a unique identifier.

    + * + * @param key The unique identifier that was used to associate the + * context object with this ELContext. + * @return The context object associated with the given key, or null + * if no such context was found. + * @throws NullPointerException if key is null. + */ + public Object getContext(Class key) { + if(key == null) { + throw new NullPointerException(); + } + return map.get(key); + } + + /** + * Retrieves the ELResolver associated with this context. + * + *

    The ELContext maintains a reference to the + * ELResolver that will be consulted to resolve variables + * and properties during an expression evaluation. This method + * retrieves the reference to the resolver.

    + * + *

    Once an ELContext is constructed, the reference to the + * ELResolver associated with the context cannot be changed.

    + * + * @return The resolver to be consulted for variable and + * property resolution during expression evaluation. + */ + public abstract ELResolver getELResolver(); + + /** + * Retrieves the ImportHandler associated with this + * ELContext. + * + * @return The import handler to manage imports of classes and packages. + * @since EL 3.0 + */ + public ImportHandler getImportHandler() { + if (importHandler == null) { + importHandler = new ImportHandler(); + } + return importHandler; + } + + /** + * Retrieves the FunctionMapper associated with this + * ELContext. + * + * @return The function mapper to be consulted for the resolution of + * EL functions. + */ + public abstract FunctionMapper getFunctionMapper(); + + /** + * Holds value of property locale. + */ + private Locale locale; + + /** + * Get the Locale stored by a previous invocation to + * {@link #setLocale}. If this method returns non null, + * this Locale must be used for all localization needs + * in the implementation. The Locale must not be cached + * to allow for applications that change Locale dynamically. + * + * @return The Locale in which this instance is operating. + * Used primarily for message localization. + */ + + public Locale getLocale() { + + return this.locale; + } + + /** + * Sets the Locale for this instance. This method may be + * called by the party creating the instance, such as JavaServer + * Faces or JSP, to enable the EL implementation to provide localized + * messages to the user. If no Locale is set, the implementation + * must use the locale returned by Locale.getDefault( ). + */ + public void setLocale(Locale locale) { + + this.locale = locale; + } + + + /** + * Retrieves the VariableMapper associated with this + * ELContext. + * + * @return The variable mapper to be consulted for the resolution of + * EL variables. + */ + public abstract VariableMapper getVariableMapper(); + + /** + * Registers an evaluation listener to the ELContext. + * + * @param listener The listener to be added. + * + * @since EL 3.0 + */ + public void addEvaluationListener(EvaluationListener listener) { + if (listeners == null) { + listeners = new ArrayList(); + } + listeners.add(listener); + } + + /** + * Returns the list of registered evaluation listeners. + * @return The list of registered evaluation listeners. + * + * @since EL 3.0 + */ + public List getEvaluationListeners() { + return listeners; + } + + /** + * Notifies the listeners before an EL expression is evaluated + * @param expr The EL expression string to be evaluated + */ + public void notifyBeforeEvaluation(String expr) { + if (getEvaluationListeners() == null) + return; + for (EvaluationListener listener: getEvaluationListeners()) { + listener.beforeEvaluation(this, expr); + } + } + + /** + * Notifies the listeners after an EL expression is evaluated + * @param expr The EL expression string that has been evaluated + */ + public void notifyAfterEvaluation(String expr) { + if (getEvaluationListeners() == null) + return; + for (EvaluationListener listener: getEvaluationListeners()) { + listener.afterEvaluation(this, expr); + } + } + + /** + * Notifies the listeners when the (base, property) pair is resolved + * @param base The base object + * @param property The property Object + */ + public void notifyPropertyResolved(Object base, Object property) { + if (getEvaluationListeners() == null) + return; + for (EvaluationListener listener: getEvaluationListeners()) { + listener.propertyResolved(this, base, property); + } + } + + /** + * Inquires if the name is a LambdaArgument + * @param arg A possible Lambda formal parameter name + * @return true if arg is a LambdaArgument, false otherwise. + */ + public boolean isLambdaArgument(String arg) { + if (lambdaArgs == null) { + return false; + } + + for (int i = lambdaArgs.size() - 1; i >= 0; i--) { + Map lmap = lambdaArgs.elementAt(i); + if (lmap.containsKey(arg)) { + return true; + } + } + return false; + } + + /** + * Retrieves the Lambda argument associated with a formal parameter. + * If the Lambda expression is nested within other Lambda expressions, the + * arguments for the current Lambda expression is first searched, and if + * not found, the arguments for the immediate nesting Lambda expression + * then searched, and so on. + * + * @param arg The formal parameter for the Lambda argument + * @return The object associated with formal parameter. Null if + * no object has been associated with the parameter. + * @since EL 3.0 + */ + public Object getLambdaArgument(String arg) { + if (lambdaArgs == null) { + return null; + } + + for (int i = lambdaArgs.size() - 1; i >= 0; i--) { + Map lmap = lambdaArgs.elementAt(i); + Object v = lmap.get(arg); + if (v != null) { + return v; + } + } + return null; + } + + /** + * Installs a Lambda argument map, in preparation for the evaluation + * of a Lambda expression. The arguments in the map will be in scope + * during the evaluation of the Lambda expression. + * @param args The Lambda arguments map + * @since EL 3.0 + */ + public void enterLambdaScope(Map args) { + if (lambdaArgs == null) { + lambdaArgs = new Stack>(); + } + lambdaArgs.push(args); + } + + /** + * Exits the Lambda expression evaluation. The Lambda argument map that + * was previously installed is removed. + * @since EL 3.0 + */ + public void exitLambdaScope() { + if (lambdaArgs != null) { + lambdaArgs.pop(); + } + } + + /** + * Converts an object to a specific type. If a custom converter in the + * ELResolver handles this conversion, it is used. Otherwise + * the standard coercions is applied. + * + *

    An ELException is thrown if an error occurs during + * the conversion.

    + * + * @param obj The object to convert. + * @param targetType The target type for the conversion. + * @throws ELException thrown if errors occur. + * + * @since EL 3.0 + */ + public Object convertToType(Object obj, + Class targetType) { + boolean propertyResolvedSave = isPropertyResolved(); + try { + setPropertyResolved(false); + ELResolver elResolver = getELResolver(); + if (elResolver != null) { + Object res = elResolver.convertToType(this, obj, targetType); + if (isPropertyResolved()) { + return res; + } + } + } catch (ELException ex) { + throw ex; + } catch (Exception ex) { + throw new ELException(ex); + } finally { + setPropertyResolved(propertyResolvedSave); + } + return ELUtil.getExpressionFactory().coerceToType(obj, targetType); + } + + private boolean resolved; + private HashMap, Object> map = new HashMap, Object>(); + private transient List listeners = null; + private Stack> lambdaArgs; + private ImportHandler importHandler; +} + diff --git a/fine-third-default/fine-javax-el/src/javax/el/ELContextEvent.java b/fine-third-default/fine-javax-el/src/javax/el/ELContextEvent.java new file mode 100644 index 000000000..797a7f865 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ELContextEvent.java @@ -0,0 +1,90 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +/** + * An event which indicates that an {@link ELContext} has been created. + * The source object is the ELContext that was created. + * + * @see ELContext + * @see ELContextListener + * @since JSP 2.1 + */ +public class ELContextEvent extends java.util.EventObject { + + /** + * Constructs an ELContextEvent object to indicate that an + * ELContext has been created. + * + * @param source the ELContext that was created. + */ + public ELContextEvent(ELContext source) { + super(source); + } + + /** + * Returns the ELContext that was created. + * This is a type-safe equivalent of the {@link #getSource} method. + * + * @return the ELContext that was created. + */ + public ELContext getELContext() { + return (ELContext) getSource(); + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ELContextListener.java b/fine-third-default/fine-javax-el/src/javax/el/ELContextListener.java new file mode 100644 index 000000000..0630ca5ea --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ELContextListener.java @@ -0,0 +1,78 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +/** + * The listener interface for receiving notification when an + * {@link ELContext} is created. + * + * @see ELContext + * @see ELContextEvent + * @since JSP 2.1 + */ +public interface ELContextListener extends java.util.EventListener { + + /** + * Invoked when a new ELContext has been created. + * + * @param ece the notification event. + */ + public void contextCreated(ELContextEvent ece); + +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ELException.java b/fine-third-default/fine-javax-el/src/javax/el/ELException.java new file mode 100644 index 000000000..cd984077e --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ELException.java @@ -0,0 +1,109 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +/** + * Represents any of the exception conditions that can arise during + * expression evaluation. + * + * @since JSP 2.1 + */ +public class ELException extends RuntimeException { + + //------------------------------------- + /** + * Creates an ELException with no detail message. + */ + public ELException () { + super (); + } + + //------------------------------------- + /** + * Creates an ELException with the provided detail message. + * + * @param pMessage the detail message + */ + public ELException (String pMessage) { + super (pMessage); + } + + //------------------------------------- + /** + * Creates an ELException with the given cause. + * + * @param pRootCause the originating cause of this exception + */ + public ELException (Throwable pRootCause) { + super( pRootCause ); + } + + //------------------------------------- + /** + * Creates an ELException with the given detail message and root cause. + * + * @param pMessage the detail message + * @param pRootCause the originating cause of this exception + */ + public ELException (String pMessage, + Throwable pRootCause) { + super (pMessage, pRootCause); + } + +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ELManager.java b/fine-third-default/fine-javax-el/src/javax/el/ELManager.java new file mode 100644 index 000000000..46e57c05a --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ELManager.java @@ -0,0 +1,191 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.el; + +import java.lang.reflect.Method; + +/** + *

    Manages EL parsing and evaluation environment. The ELManager maintains an + * instance of ExpressionFactory and StandardELContext, for + * parsing and evaluating EL expressions.

    + * + * @since EL 3.0 + */ +public class ELManager { + + private StandardELContext elContext; + + /** + * Return the ExpressionFactory instance used for EL evaluations. + * @return The ExpressionFactory + */ + public static ExpressionFactory getExpressionFactory() { + return ELUtil.getExpressionFactory(); + } + + /** + * Return the ELContext used for parsing and evaluating EL expressions. + * If there is currently no ELContext, a default instance of + * StandardELContext is returned. + * + * @return The ELContext used for parsing and evaluating EL expressions.. + */ + public StandardELContext getELContext() { + if (elContext == null) { + elContext = new StandardELContext(getExpressionFactory()); + } + return elContext; + } + + /** + * Set the ELContext used for parsing and evaluating EL expressions. + * The supplied ELContext will not be modified, except for the context + * object map. + * @param context The new ELContext. + * @return The previous ELContext, null if none. + */ + public ELContext setELContext(ELContext context) { + ELContext prev = elContext; + elContext = new StandardELContext(context); + return prev; + } + + /** + * Register a BeanNameResolver. + * Construct a BeanNameELResolver with the BeanNameResolver and add it + * to the list of ELResolvers. + * Once registered, the BeanNameResolver cannot be removed. + * @param bnr The BeanNameResolver to be registered. + */ + public void addBeanNameResolver(BeanNameResolver bnr) { + getELContext().addELResolver(new BeanNameELResolver(bnr)); + } + + /** + * Add an user defined ELResolver to the list of ELResolvers. + * Can be called multiple times. The new ELResolver is + * placed ahead of the default ELResolvers. The list of the ELResolvers + * added this way are ordered chronologically. + * + * @param elr The ELResolver to be added to the list of ELResolvers in + * ELContext. + * @see StandardELContext#addELResolver + */ + public void addELResolver(ELResolver elr) { + getELContext().addELResolver(elr); + } + + /** + * Maps a static method to an EL function. + * @param prefix The namespace of the functions, can be "". + * @param function The name of the function. + * @param meth The static method to be invoked when the function is used. + */ + public void mapFunction(String prefix, String function, Method meth) { + getELContext().getFunctionMapper().mapFunction(prefix, function, meth); + } + + /** + * Assign a ValueExpression to an EL variable, replacing + * any previous assignment to the same variable. + * The assignment for the variable is removed if + * the expression is null. + * + * @param variable The variable name + * @param expression The ValueExpression to be assigned + * to the variable. + */ + public void setVariable(String variable, ValueExpression expression) { + getELContext().getVariableMapper().setVariable(variable, expression); + } + + /** + * Import a static field or method. The class of the static member must be + * loadable from the classloader, at class resolution time. + * @param staticMemberName The full class name of the class to be imported + * @throws ELException if the name is not a full class name. + */ + public void importStatic(String staticMemberName) throws ELException { + getELContext().getImportHandler().importStatic(staticMemberName); + } + + /** + * Import a class. The imported class must be loadable from the classloader + * at the expression evaluation time. + * @param className The full class name of the class to be imported + * @throws ELException if the name is not a full class name. + */ + public void importClass(String className) throws ELException { + getELContext().getImportHandler().importClass(className); + } + + /** + * Import a package. At the expression evaluation time, the imported package + * name will be used to construct the full class name, which will then be + * used to load the class. Inherently, this is less efficient than + * importing a class. + * @param packageName The package name to be imported + */ + public void importPackage(String packageName) { + getELContext().getImportHandler().importPackage(packageName); + } + + /** + * Define a bean in the local bean repository + * @param name The name of the bean + * @param bean The bean instance to be defined. If null, the definition + * of the bean is removed. + */ + public Object defineBean(String name, Object bean) { + Object ret = getELContext().getBeans().get(name); + getELContext().getBeans().put(name, bean); + return ret; + } + + /** + * Register an evaluation listener. + * + * @param listener The evaluation listener to be added. + */ + public void addEvaluationListener(EvaluationListener listener) { + getELContext().addEvaluationListener(listener); + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ELProcessor.java b/fine-third-default/fine-javax-el/src/javax/el/ELProcessor.java new file mode 100644 index 000000000..b04f241c1 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ELProcessor.java @@ -0,0 +1,334 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.el; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + *

    Provides an API for using EL in a stand-alone environment.

    + * + *

    This class provides a direct and simple interface for + *

      + *
    • Evaluating EL expressions.
    • + *
    • Assigning values to beans or setting a bean property.
    • + *
    • Setting a {@link ValueExpression} to a EL variable.
    • + *
    • Defining a static method as an EL function.
    • + *
    • Defining an object instance as an EL name. + *
    + * + *

    This API is not a replacement for the APIs in EL 2.2. Containers that + * maintains EL environments can continue to do so, without using this API.

    + * + *

    For EL users who want to manipulate EL environments, like adding custom + * {@link ELResolver}s, {@link ELManager} can be used.

    + * + *

    Scope and Life Cycle

    + *

    Since it maintains the state of the EL environments, + * ELProcessor is not thread safe. In the simplest case, + * an instance can be created and destroyed before and after evaluating + * EL expressions. A more general usage is to use an instance of + * ELProcessor for a session, so that the user can configure the + * EL evaluation environment for that session.

    + * + *

    Automatic Bracketing of Expressions

    + *

    A note about the EL expressions strings used in the class. The strings + * allowed in the methods {@link ELProcessor#getValue}, + * {@link ELProcessor#setValue}, and {@link ELProcessor#setVariable} are + * limited to non-composite expressions, i.e. expressions + * of the form ${...} or #{...} only. Also, it is not necessary (in fact not + * allowed) to bracket the expression strings with ${ or #{ and } in these + * methods: they will be automatically bracketed. This reduces the visual + * cluster, without any lost of functionalities (thanks to the addition of the + * concatenation operator). + * + *

    Example

    + * The following code snippet illustrates the use of ELProcessor to define + * a bean and evaluate its property. + *
    + *
    + *   ELProcessor elp = new ELProcessor();
    + *   elp.defineBean("employee", new Employee("Charlie Brown"));
    + *   String name = elp.eval("employee.name");
    + * 
    + *
    + * @since EL 3.0 + */ + +public class ELProcessor { + + private ELManager elManager = new ELManager(); + private ExpressionFactory factory = elManager.getExpressionFactory(); + + /** + * Return the ELManager used for EL processing. + * @return The ELManager used for EL processing. + */ + public ELManager getELManager() { + return elManager; + } + + /** + * Evaluates an EL expression. + * @param expression The EL expression to be evaluated. + * @return The result of the expression evaluation. + */ + public Object eval(String expression) { + return getValue(expression, Object.class); + } + + /** + * Evaluates an EL expression, and coerces the result to the specified type. + * @param expression The EL expression to be evaluated. + * @param expectedType Specifies the type that the resultant evaluation + * will be coerced to. + * @return The result of the expression evaluation. + */ + public Object getValue(String expression, Class expectedType) { + ValueExpression exp = factory.createValueExpression( + elManager.getELContext(), + bracket(expression), expectedType); + return exp.getValue(elManager.getELContext()); + } + + /** + * Sets an expression with a new value. + * The target expression is evaluated, up to the last property resolution, + * and the resultant (base, property) pair is set to the provided value. + * + * @param expression The target expression + * @param value The new value to set. + * @throws PropertyNotFoundException if one of the property + * resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws PropertyNotWritableException if the final variable or + * property resolution failed because the specified + * variable or property is not writable. + * @throws ELException if an exception was thrown while attempting to + * set the property or variable. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public void setValue(String expression, Object value) { + ValueExpression exp = factory.createValueExpression( + elManager.getELContext(), + bracket(expression), Object.class); + exp.setValue(elManager.getELContext(), value); + } + + /** + * Assign an EL expression to an EL variable. The expression is parsed, + * but not evaluated, and the parsed expression is mapped to the EL + * variable in the local variable map. + * Any previously assigned expression to the same variable will be replaced. + * If the expression is null, the variable will be removed. + * @param var The name of the variable. + * @param expression The EL expression to be assigned to the variable. + */ + public void setVariable(String var, String expression) { + ValueExpression exp = factory.createValueExpression( + elManager.getELContext(), + bracket(expression), Object.class); + elManager.setVariable(var, exp); + } + + /** + * Define an EL function in the local function mapper. + * @param prefix The namespace for the function or "" for no namesapce. + * @param function The name of the function. + * If empty (""), the method name is used as the function name. + * @param className The full Java class name that implements the function. + * @param method The name (specified without parenthesis) or the signature + * (as in the Java Language Spec) of the static method that implements + * the function. If the name (e.g. "sum") is given, the first declared + * method in class that matches the name is selected. If the signature + * (e.g. "int sum(int, int)" ) is given, then the declared method + * with the signature is selected. + * + * @throws NullPointerException if any of the arguments is null. + * @throws ClassNotFoundException if the specified class does not exists. + * @throws NoSuchMethodException if the method (with or without the + * signature) is not a declared method of the class, or if the method + * signature is not valid, or if the method is not a static method. + */ + public void defineFunction(String prefix, String function, + String className, + String method) + throws ClassNotFoundException, NoSuchMethodException { + + if (prefix == null || function == null || className == null + || method == null) { + throw new NullPointerException("Null argument for defineFunction"); + } + + Method meth = null; + ClassLoader loader = getClass().getClassLoader(); + Class klass = Class.forName(className, false, loader); + int j = method.indexOf('('); + if (j < 0) { + // Just a name is given + for (Method m: klass.getDeclaredMethods()) { + if (m.getName().equals(method)) { + meth = m; + } + } + if (meth == null) { + throw new NoSuchMethodException(); + } + } else { + // method is the signature + // First get the method name, ignore the return type + int p = method.indexOf(' '); + if (p < 0) { + throw new NoSuchMethodException( + "Bad method singnature: " + method); + } + String methodName = method.substring(p+1, j).trim(); + // Extract parameter types + p = method.indexOf(')', j+1); + if (p < 0) { + throw new NoSuchMethodException( + "Bad method singnature: " + method); + } + String[] params = method.substring(j+1, p).split(","); + Class[] paramTypes = new Class[params.length]; + for (int i = 0; i < params.length; i++) { + paramTypes[i] = toClass(params[i], loader); + } + meth = klass.getDeclaredMethod(methodName, paramTypes); + } + if (! Modifier.isStatic(meth.getModifiers())) { + throw new NoSuchMethodException("The method specified in defineFunction must be static: " + meth); + } + if (function.equals("")) { + function = method; + } + elManager.mapFunction(prefix, function, meth); + } + + /** + * Define an EL function in the local function mapper. + * @param prefix The namespace for the function or "" for no namesapce. + * @param function The name of the function. + * If empty (""), the method name is used as the function name. + * @param method The java.lang.reflect.Method instance of + * the method that implements the function. + * @throws NullPointerException if any of the arguments is null. + * @throws NoSuchMethodException if the method is not a static method + */ + public void defineFunction(String prefix, String function, Method method) + throws NoSuchMethodException { + if (prefix == null || function == null || method == null) { + throw new NullPointerException("Null argument for defineFunction"); + } + if (! Modifier.isStatic(method.getModifiers())) { + throw new NoSuchMethodException("The method specified in defineFunction must be static: " + method); + } + if (function.equals("")) { + function = method.getName(); + } + elManager.mapFunction(prefix, function, method); + } + + /** + * Define a bean in a local bean repository, hiding other beans of the + * same name. + * @param name The name of the bean + * @param bean The bean instance to be defined. If null, + * the name will be removed from the local bean repository. + */ + public void defineBean(String name, Object bean) { + elManager.defineBean(name, bean); + } + + /** + * Return the Class object associated with the class or interface with + * the given name. + */ + private static Class toClass(String type, ClassLoader loader) + throws ClassNotFoundException { + + Class c = null; + int i0 = type.indexOf('['); + int dims = 0; + if (i0 > 0) { + // This is an array. Count the dimensions + for (int i = 0; i < type.length(); i++) { + if (type.charAt(i) == '[') + dims++; + } + type = type.substring(0, i0); + } + + if ("boolean".equals(type)) + c = boolean.class; + else if ("char".equals(type)) + c = char.class; + else if ("byte".equals(type)) + c = byte.class; + else if ("short".equals(type)) + c = short.class; + else if ("int".equals(type)) + c = int.class; + else if ("long".equals(type)) + c = long.class; + else if ("float".equals(type)) + c = float.class; + else if ("double".equals(type)) + c = double.class; + else + c = loader.loadClass(type); + + if (dims == 0) + return c; + + if (dims == 1) + return java.lang.reflect.Array.newInstance(c, 1).getClass(); + + // Array of more than i dimension + return java.lang.reflect.Array.newInstance(c, new int[dims]).getClass(); + } + + private String bracket(String expression) { + return "${" + expression + '}'; + } +} + diff --git a/fine-third-default/fine-javax-el/src/javax/el/ELResolver.java b/fine-third-default/fine-javax-el/src/javax/el/ELResolver.java new file mode 100644 index 000000000..d6fd535d4 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ELResolver.java @@ -0,0 +1,454 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.util.Iterator; +import java.beans.FeatureDescriptor; + +/** + * Enables customization of variable, property, method call, and type + * conversion resolution behavior for EL expression evaluation. + * + *

    While evaluating an expression, the ELResolver associated + * with the {@link ELContext} is consulted to do the initial resolution of + * the first variable of an expression. It is also consulted when a + * . or [] operator is encountered. + * + *

    For example, in the EL expression ${employee.lastName}, + * the ELResolver determines what object employee + * refers to, and what it means to get the lastName property on + * that object.

    + * + *

    Most methods in this class accept a base + * and property parameter. In the case of variable resolution + * (e.g. determining what employee refers to in + * ${employee.lastName}), the base parameter will + * be null and the property parameter will always + * be of type String. In this case, if the property + * is not a String, the behavior of the ELResolver + * is undefined.

    + * + *

    In the case of property resolution, the base parameter + * identifies the base object and the property object identifies + * the property on that base. For example, in the expression + * ${employee.lastName}, base is the result of the + * variable resolution for employee and property + * is the string "lastName". In the expression + * ${y[x]}, base is the result of the variable + * resolution for y and property is the result of + * the variable resolution for x.

    + * + *

    In the case of method call resolution, the base parameter + * identifies the base object and the method parameter identifies + * a method on that base. In the case of overloaded methods, the + * paramTypes parameter can be optionally used to identify a method. + * The paramsparameter are the parameters for the method call, + * and can also be used for resolving overloaded methods when the + * paramTypes parameter is not specified. + * + *

    In the case of type conversion resolution, the obj parameter + * identifies the source object and the targetType parameter + * identifies the target type the source to covert to. + * + *

    Though only a single ELResolver is associated with an + * ELContext, there are usually multiple resolvers considered + * for any given variable or property resolution. ELResolvers + * are combined together using {@link CompositeELResolver}s, to define + * rich semantics for evaluating an expression.

    + * + *

    For the {@link #getValue}, {@link #getType}, {@link #setValue}, and + * {@link #isReadOnly} methods, an ELResolver is not + * responsible for resolving all possible (base, property) pairs. In fact, + * most resolvers will only handle a base of a single type. + * To indicate that a resolver has successfully resolved a particular + * (base, property) pair, it must set the propertyResolved + * property of the ELContext to true. If it could + * not handle the given pair, it must leave this property alone. The caller + * must ignore the return value of the method if propertyResolved + * is false.

    + * + *

    Similarly, for the {@link #convertToType} method an + * ELResolver + * must set the propertyResolved to true to indicate + * that it handles the conversion of the object to the target type.

    + * + *

    The {@link #getFeatureDescriptors} and {@link #getCommonPropertyType} + * methods are primarily designed for design-time tool support, but must + * handle invocation at runtime as well. The + * {@link java.beans.Beans#isDesignTime} method can be used to determine + * if the resolver is being consulted at design-time or runtime.

    + * + * @see CompositeELResolver + * @see ELContext#getELResolver + * @since JSP 2.1 + */ +public abstract class ELResolver { + + // --------------------------------------------------------- Constants + + /** + *

    The attribute name of the named attribute in the + * FeatureDescriptor that specifies the runtime type of + * the variable or property.

    + */ + + public static final String TYPE = "type"; + + /** + *

    The attribute name of the named attribute in the + * FeatureDescriptor that specifies whether the + * variable or property can be resolved at runtime.

    + */ + + public static final String RESOLVABLE_AT_DESIGN_TIME = "resolvableAtDesignTime"; + + /** + * Attempts to resolve the given property object on the given + * base object. + * + *

    If this resolver handles the given (base, property) pair, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller should ignore + * the return value.

    + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be returned, + * or null to resolve a top-level variable. + * @param property The property or variable to be resolved. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the result of the variable or property resolution; otherwise + * undefined. + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if the given (base, property) pair + * is handled by this ELResolver but the specified + * variable or property does not exist or is not readable. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public abstract Object getValue(ELContext context, + Object base, + Object property); + + /** + * Attempts to resolve and invoke the given method on the given + * base object. + * + *

    If this resolver handles the given (base, method) pair, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller should ignore + * the return value.

    + * + *

    A default implementation is provided that returns null so that + * existing classes that extend ELResolver can continue to function.

    + * + * @param context The context of this evaluation. + * @param base The bean on which to invoke the method + * @param method The simple name of the method to invoke. + * Will be coerced to a String. + * @param paramTypes An array of Class objects identifying the + * method's formal parameter types, in declared order. + * Use an empty array if the method has no parameters. + * Can be null, in which case the method's formal + * parameter types are assumed to be unknown. + * @param params The parameters to pass to the method, or + * null if no parameters. + * @return The result of the method invocation (null if + * the method has a void return type). + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if an exception was thrown while performing + * (base, method) resolution. The thrown exception must be + * included as the cause property of this exception, if + * available. If the exception thrown is an + * InvocationTargetException, extract its + * cause and pass it to the + * ELException constructor. + * @since EL 2.2 + */ + public Object invoke(ELContext context, + Object base, + Object method, + Class[] paramTypes, + Object[] params) { + return null; + } + + + /** + * For a given base and property, attempts to + * identify the most general type that is acceptable for an object to be + * passed as the value parameter in a future call + * to the {@link #setValue} method. + * + *

    If this resolver handles the given (base, property) pair, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller should ignore + * the return value.

    + * + *

    This is not always the same as getValue().getClass(). + * For example, in the case of an {@link ArrayELResolver}, the + * getType method will return the element type of the + * array, which might be a superclass of the type of the actual + * element that is currently in the specified array element.

    + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be analyzed, + * or null to analyze a top-level variable. + * @param property The property or variable to return the acceptable + * type for. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the most general acceptable type; otherwise undefined. + * @throws PropertyNotFoundException if the given (base, property) pair + * is handled by this ELResolver but the specified + * variable or property does not exist or is not readable. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public abstract Class getType(ELContext context, + Object base, + Object property); + + /** + * Attempts to set the value of the given property + * object on the given base object. + * + *

    If this resolver handles the given (base, property) pair, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller can + * safely assume no value has been set.

    + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be set, + * or null to set a top-level variable. + * @param property The property or variable to be set. + * @param value The value to set the property or variable to. + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if the given (base, property) pair + * is handled by this ELResolver but the specified + * variable or property does not exist. + * @throws PropertyNotWritableException if the given (base, property) + * pair is handled by this ELResolver but the specified + * variable or property is not writable. + * @throws ELException if an exception was thrown while attempting to + * set the property or variable. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public abstract void setValue(ELContext context, + Object base, + Object property, + Object value); + + + /** + * For a given base and property, attempts to + * determine whether a call to {@link #setValue} will always fail. + * + *

    If this resolver handles the given (base, property) pair, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller should ignore + * the return value.

    + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be analyzed, + * or null to analyze a top-level variable. + * @param property The property or variable to return the read-only status + * for. + * @return If the propertyResolved property of + * ELContext was set to true, then + * true if the property is read-only or + * false if not; otherwise undefined. + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if the given (base, property) pair + * is handled by this ELResolver but the specified + * variable or property does not exist. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public abstract boolean isReadOnly(ELContext context, + Object base, + Object property); + + /** + * Returns information about the set of variables or properties that + * can be resolved for the given base object. One use for + * this method is to assist tools in auto-completion. + * + *

    If the base parameter is null, the + * resolver must enumerate the list of top-level variables it can + * resolve.

    + * + *

    The Iterator returned must contain zero or more + * instances of {@link java.beans.FeatureDescriptor}, in no guaranteed + * order. In the case of primitive types such as int, the + * value null must be returned. This is to prevent the + * useless iteration through all possible primitive values. A + * return value of null indicates that this resolver does + * not handle the given base object or that the results + * are too complex to represent with this method and the + * {@link #getCommonPropertyType} method should be used instead.

    + * + *

    Each FeatureDescriptor will contain information about + * a single variable or property. In addition to the standard + * properties, the FeatureDescriptor must have two + * named attributes (as set by the setValue method): + *

      + *
    • {@link #TYPE} - The value of this named attribute must be + * an instance of java.lang.Class and specify the + * runtime type of the variable or property.
    • + *
    • {@link #RESOLVABLE_AT_DESIGN_TIME} - The value of this + * named attribute must be an instance of + * java.lang.Boolean and indicates whether it is safe + * to attempt to resolve this property at design-time. For + * instance, it may be unsafe to attempt a resolution at design + * time if the ELResolver needs access to a resource + * that is only available at runtime and no acceptable simulated + * value can be provided.
    • + *

    + * + *

    The caller should be aware that the Iterator + * returned might iterate through a very large or even infinitely large + * set of properties. Care should be taken by the caller to not get + * stuck in an infinite loop.

    + * + *

    This is a "best-effort" list. Not all ELResolvers + * will return completely accurate results, but all must be callable + * at both design-time and runtime (i.e. whether or not + * Beans.isDesignTime() returns true), + * without causing errors.

    + * + *

    The propertyResolved property of the + * ELContext is not relevant to this method. + * The results of all ELResolvers are concatenated + * in the case of composite resolvers.

    + * + * @param context The context of this evaluation. + * @param base The base object whose set of valid properties is to + * be enumerated, or null to enumerate the set of + * top-level variables that this resolver can evaluate. + * @return An Iterator containing zero or more (possibly + * infinitely more) FeatureDescriptor objects, or + * null if this resolver does not handle the given + * base object or that the results are too complex to + * represent with this method + * @see java.beans.FeatureDescriptor + */ + public abstract Iterator getFeatureDescriptors( + ELContext context, + Object base); + + /** + * Returns the most general type that this resolver accepts for the + * property argument, given a base object. + * One use for this method is to assist tools in auto-completion. + * + *

    This assists tools in auto-completion and also provides a + * way to express that the resolver accepts a primitive value, + * such as an integer index into an array. For example, the + * {@link ArrayELResolver} will accept any int as a + * property, so the return value would be + * Integer.class.

    + * + * @param context The context of this evaluation. + * @param base The base object to return the most general property + * type for, or null to enumerate the set of + * top-level variables that this resolver can evaluate. + * @return null if this ELResolver does not + * know how to handle the given base object; otherwise + * Object.class if any type of property + * is accepted; otherwise the most general property + * type accepted for the given base. + */ + public abstract Class getCommonPropertyType(ELContext context, + Object base); + + /** + * Converts an object to a specific type. + * + *

    An ELException is thrown if an error occurs during + * the conversion.

    + * + * @param context The context of this evaluation. + * @param obj The object to convert. + * @param targetType The target type for the convertion. + * @throws ELException thrown if errors occur. + */ + public Object convertToType(ELContext context, + Object obj, + Class targetType) { + return null; + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ELUtil.java b/fine-third-default/fine-javax-el/src/javax/el/ELUtil.java new file mode 100644 index 000000000..8529db67d --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ELUtil.java @@ -0,0 +1,333 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * + *

    Utility methods for this portion of the EL implementation

    + * + *

    Methods on this class use a Map instance stored in ThreadLocal storage + * to minimize the performance impact on operations that take place multiple + * times on a single Thread. The keys and values of the Map + * are implementation private.

    + * + * @author edburns + * @author Kin-man Chung + */ +class ELUtil { + + /** + *

    This class may not be constructed.

    + */ + + private ELUtil() { + } + + public static ExpressionFactory exprFactory = + ExpressionFactory.newInstance(); + + /** + *

    The ThreadLocal variable used to record the + * {@link javax.faces.context.FacesContext} instance for each + * processing thread.

    + */ + private static ThreadLocal> instance = + new ThreadLocal>() { + protected Map initialValue() { + return (null); + } + }; + + /** + * @return a Map stored in ThreadLocal storage. This may + * be used by methods of this class to minimize the performance + * impact for operations that may take place multiple times on a given + * Thread instance. + */ + + private static Map getCurrentInstance() { + Map result = instance.get(); + if (null == result) { + result = new HashMap(); + setCurrentInstance(result); + } + return result; + + } + + /** + *

    Replace the Map with the argument context.

    + * + * @param context the Map to be stored in ThreadLocal storage. + */ + + private static void setCurrentInstance(Map context) { + + instance.set(context); + + } + + /* + *

    Convenience method, calls through to + * {@link #getExceptionMessageString(javax.el.ELContext,java.lang.String,Object []). + *

    + * + * @param context the ELContext from which the Locale for this message + * is extracted. + * + * @param messageId the messageId String in the ResourceBundle + * + * @return a localized String for the argument messageId + */ + + public static String getExceptionMessageString(ELContext context, String messageId) { + return getExceptionMessageString(context, messageId, null); + } + + /* + *

    Return a Localized message String suitable for use as an Exception message. + * Examine the argument context for a Locale. If + * not present, use Locale.getDefault(). Load the + * ResourceBundle "javax.el.Messages" using that locale. Get + * the message string for argument messageId. If not found + * return "Missing Resource in EL implementation ??? messageId ???" + * with messageId substituted with the runtime + * value of argument messageId. If found, and argument + * params is non-null, format the message using the + * params. If formatting fails, return a sensible message including + * the messageId. If argument params is + * null, skip formatting and return the message directly, otherwise + * return the formatted message.

    + * + * @param context the ELContext from which the Locale for this message + * is extracted. + * + * @param messageId the messageId String in the ResourceBundle + * + * @param params parameters to the message + * + * @return a localized String for the argument messageId + */ + + public static String getExceptionMessageString(ELContext context, + String messageId, + Object [] params) { + String result = ""; + Locale locale = null; + + if (null == context || null == messageId) { + return result; + } + + if (null == (locale = context.getLocale())) { + locale = Locale.getDefault(); + } + if (null != locale) { + Map threadMap = getCurrentInstance(); + ResourceBundle rb = null; + if (null == (rb = (ResourceBundle) + threadMap.get(locale.toString()))) { + rb = ResourceBundle.getBundle("javax.el.PrivateMessages", + locale); + threadMap.put(locale.toString(), rb); + } + if (null != rb) { + try { + result = rb.getString(messageId); + if (null != params) { + result = MessageFormat.format(result, params); + } + } catch (IllegalArgumentException iae) { + result = "Can't get localized message: parameters to message appear to be incorrect. Message to format: " + messageId; + } catch (MissingResourceException mre) { + result = "Missing Resource in EL implementation: ???" + messageId + "???"; + } catch (Exception e) { + result = "Exception resolving message in EL implementation: ???" + messageId + "???"; + } + } + } + + return result; + } + + static ExpressionFactory getExpressionFactory() { + return exprFactory; + } + + static Constructor findConstructor(Class klass, + Class[] paramTypes, + Object[] params) { + + if (paramTypes != null) { + try { + Constructor c = klass.getConstructor(paramTypes); + if (Modifier.isPublic(c.getModifiers())) { + return c; + } + } catch (java.lang.NoSuchMethodException ex) { + } + throw new MethodNotFoundException("The constructor for class " + + klass + " not found or accessible"); + } + + int paramCount = (params == null)? 0: params.length; + for (Constructor c: klass.getConstructors()) { + if (c.isVarArgs() || c.getParameterTypes().length==paramCount) { + return c; + } + } + throw new MethodNotFoundException("The constructor for class " + + klass + " not found"); + } + + static Object invokeConstructor(ELContext context, + Constructor c, + Object[] params) { + Class[] parameterTypes = c.getParameterTypes(); + Object[] parameters = null; + if (parameterTypes.length > 0) { + if (c.isVarArgs()) { + // TODO + } else { + parameters = new Object[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + parameters[i] = context.convertToType(params[i], + parameterTypes[i]); + } + } + } + try { + return c.newInstance(parameters); + } catch (IllegalAccessException iae) { + throw new ELException(iae); + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } catch (InstantiationException ie) { + throw new ELException(ie.getCause()); + } + } + + static Method findMethod(Class klass, + String method, + Class[] paramTypes, + Object[] params, + boolean staticOnly) { + + if (paramTypes != null) { + try { + Method m = klass.getMethod(method, paramTypes); + int mod = m.getModifiers(); + if (Modifier.isPublic(mod) && + (!staticOnly || Modifier.isStatic(mod))) { + return m; + } + } catch (java.lang.NoSuchMethodException ex) { + } + throw new MethodNotFoundException("Method " + method + + "for class " + klass + + " not found or accessible"); + } + + int paramCount = (params == null)? 0: params.length; + for (Method m: klass.getMethods()) { + if (m.getName().equals(method) && ( + m.isVarArgs() || + m.getParameterTypes().length==paramCount)){ + return m; + } + } + throw new MethodNotFoundException("Method " + method + " not found"); + } + + static Object invokeMethod(ELContext context, + Method m, Object base, Object[] params) { + + Class[] parameterTypes = m.getParameterTypes(); + Object[] parameters = null; + if (parameterTypes.length > 0) { + if (m.isVarArgs()) { + // TODO + } else { + parameters = new Object[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + parameters[i] = context.convertToType(params[i], + parameterTypes[i]); + } + } + } + try { + return m.invoke(base, parameters); + } catch (IllegalAccessException iae) { + throw new ELException(iae); + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/EvaluationListener.java b/fine-third-default/fine-javax-el/src/javax/el/EvaluationListener.java new file mode 100644 index 000000000..ce49c23a5 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/EvaluationListener.java @@ -0,0 +1,76 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.el; + +/** + * The listener interface for receiving notification when an + * EL expression is evaluated. + * + * @since EL 3.0 + */ +public abstract class EvaluationListener { + + /** + * Receives notification before an EL expression is evaluated + * @param context The ELContext + * @param expression The EL expression string to be evaluated + */ + public void beforeEvaluation(ELContext context, String expression) { + } + + /** + * Receives notification after an EL expression is evaluated + * @param context The ELContext + * @param expression The EL expression string to be evaluated + */ + public void afterEvaluation(ELContext context, String expression) { + } + + /** + * Receives notification when the (base, property) pair is resolved + * @param context The ELContext + * @param base The base object + * @param property The property object + */ + public void propertyResolved(ELContext context, Object base, Object property) { + } + +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/Expression.java b/fine-third-default/fine-javax-el/src/javax/el/Expression.java new file mode 100644 index 000000000..521ed87b8 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/Expression.java @@ -0,0 +1,160 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.io.Serializable; + +/** + * Base class for the expression subclasses {@link ValueExpression} and + * {@link MethodExpression}, implementing characteristics common to both. + * + *

    All expressions must implement the equals() and + * hashCode() methods so that two expressions can be compared + * for equality. They are redefined abstract in this class to force their + * implementation in subclasses.

    + * + *

    All expressions must also be Serializable so that they + * can be saved and restored.

    + * + *

    Expressions are also designed to be immutable so + * that only one instance needs to be created for any given expression + * String / {@link FunctionMapper}. This allows a container to pre-create + * expressions and not have to re-parse them each time they are evaluated.

    + * + * @since JSP 2.1 + */ +public abstract class Expression + implements Serializable { + // Debugging + + /** + * Returns the original String used to create this Expression, + * unmodified. + * + *

    This is used for debugging purposes but also for the purposes + * of comparison (e.g. to ensure the expression in a configuration + * file has not changed).

    + * + *

    This method does not provide sufficient information to + * re-create an expression. Two different expressions can have exactly + * the same expression string but different function mappings. + * Serialization should be used to save and restore the state of an + * Expression.

    + * + * @return The original expression String. + */ + public abstract String getExpressionString(); + + // Comparison + + /** + * Determines whether the specified object is equal to this + * Expression. + * + *

    The result is true if and only if the argument is + * not null, is an Expression object that + * is the of the same type (ValueExpression or + * MethodExpression), and has an identical parsed + * representation.

    + * + *

    Note that two expressions can be equal if their expression + * Strings are different. For example, ${fn1:foo()} + * and ${fn2:foo()} are equal if their corresponding + * FunctionMappers mapped fn1:foo and + * fn2:foo to the same method.

    + * + * @param obj the Object to test for equality. + * @return true if obj equals this + * Expression; false otherwise. + * @see java.util.Hashtable + * @see java.lang.Object#equals(java.lang.Object) + */ + public abstract boolean equals(Object obj); + + /** + * Returns the hash code for this Expression. + * + *

    See the note in the {@link #equals} method on how two expressions + * can be equal if their expression Strings are different. Recall that + * if two objects are equal according to the equals(Object) + * method, then calling the hashCode method on each of the + * two objects must produce the same integer result. Implementations must + * take special note and implement hashCode correctly.

    + * + * @return The hash code for this Expression. + * @see #equals + * @see java.util.Hashtable + * @see java.lang.Object#hashCode() + */ + public abstract int hashCode(); + + /** + * Returns whether this expression was created from only literal text. + * + *

    This method must return true if and only if the + * expression string this expression was created from contained no + * unescaped EL delimeters (${...} or + * #{...}).

    + * + * @return true if this expression was created from only + * literal text; false otherwise. + */ + public abstract boolean isLiteralText(); +} + diff --git a/fine-third-default/fine-javax-el/src/javax/el/ExpressionFactory.java b/fine-third-default/fine-javax-el/src/javax/el/ExpressionFactory.java new file mode 100644 index 000000000..ddf35583a --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ExpressionFactory.java @@ -0,0 +1,374 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.util.Map; +import java.lang.reflect.Method; + +import java.util.Properties; + +/** + * Provides an implementation for creating and evaluating EL expressions. + * + *

    Classes that implement the EL expression language expose their + * functionality via this abstract class. An implementation supports the + * following functionalities. + *

      + *
    • + * Parses a String into a {@link ValueExpression} or + * {@link MethodExpression} instance for later evaluation. + *
    • + *
    • Implements an ELResolver for query operators
    • + *
    • Provides a default type coercion
    • + *
    + *

    + *

    The {@link #newInstance} method can be used to obtain an + * instance of the implementation. + * Technologies such as + * JavaServer Pages and JavaServer Faces provide access to an + * implementation via factory methods.

    + * + *

    The {@link #createValueExpression} method is used to parse expressions + * that evaluate to values (both l-values and r-values are supported). + * The {@link #createMethodExpression} method is used to parse expressions + * that evaluate to a reference to a method on an object.

    + * + *

    Resolution of model objects is performed at evaluation time, via the + * {@link ELResolver} associated with the {@link ELContext} passed to + * the ValueExpression or MethodExpression.

    + * + *

    The ELContext object also provides access to the {@link FunctionMapper} + * and {@link VariableMapper} to be used when parsing the expression. + * EL function and variable mapping is performed at parse-time, and + * the results are + * bound to the expression. Therefore, the {@link ELContext}, + * {@link FunctionMapper}, + * and {@link VariableMapper} + * are not stored for future use and do not have to be + * Serializable.

    + * + *

    The createValueExpression and + * createMethodExpression methods must be thread-safe. That is, + * multiple threads may call these methods on the same + * ExpressionFactory object simultaneously. Implementations + * should synchronize access if they depend on transient state. + * Implementations should not, however, assume that only one object of + * each ExpressionFactory type will be instantiated; global + * caching should therefore be static.

    + * + *

    The ExpressionFactory must be able to handle the following + * types of input for the expression parameter: + *

      + *
    • Single expressions using the ${} delimiter + * (e.g. "${employee.lastName}").
    • + *
    • Single expressions using the #{} delimiter + * (e.g. "#{employee.lastName}").
    • + *
    • Literal text containing no ${} or #{} + * delimiters (e.g. "John Doe").
    • + *
    • Multiple expressions using the same delimiter (e.g. + * "${employee.firstName}${employee.lastName}" or + * "#{employee.firstName}#{employee.lastName}").
    • + *
    • Mixed literal text and expressions using the same delimiter (e.g. + * "Name: ${employee.firstName} ${employee.lastName}").
    • + *

    + * + *

    The following types of input are illegal and must cause an + * {@link ELException} to be thrown: + *

      + *
    • Multiple expressions using different delimiters (e.g. + * "${employee.firstName}#{employee.lastName}").
    • + *
    • Mixed literal text and expressions using different delimiters(e.g. + * "Name: ${employee.firstName} #{employee.lastName}").
    • + *

    + * + * @since JSP 2.1 + */ + +public abstract class ExpressionFactory { + + /** + * Creates a new instance of a ExpressionFactory. + * This method uses the following ordered lookup procedure to determine + * the ExpressionFactory implementation class to load: + *
      + *
    • Use the Services API (as detailed in the JAR specification). + * If a resource with the name of + * META-INF/services/javax.el.ExpressionFactory exists, + * then its first line, if present, is used as the UTF-8 encoded name of + * the implementation class.
    • + *
    • Use the properties file "lib/el.properties" in the JRE directory. + * If this file exists and it is readable by the + * java.util.Properties.load(InputStream) method, + * and it contains an entry whose key is "javax.el.ExpressionFactory", + * then the value of that entry is used as the name of the + * implementation class.
    • + *
    • Use the javax.el.ExpressionFactory system property. + * If a system property with this name is defined, then its value is + * used as the name of the implementation class.
    • + *
    • Use a platform default implementation.
    • + *
    + */ + public static ExpressionFactory newInstance() { + return ExpressionFactory.newInstance(null); + } + + /** + *

    Create a new instance of a ExpressionFactory, with + * optional properties. + * This method uses the same lookup procedure as the one used in + * newInstance(). + *

    + *

    + * If the argument properties is not null, and if the + * implementation contains a constructor with a single parameter of + * type java.util.Properties, then the constructor is used + * to create the instance. + *

    + *

    + * Properties are optional and can be ignored by an implementation. + *

    + *

    The name of a property should start with "javax.el."

    + *

    + * The following are some suggested names for properties. + *

      + *
    • javax.el.cacheSize
    • + *

    + * + * @param properties Properties passed to the implementation. + * If null, then no properties. + */ + public static ExpressionFactory newInstance(Properties properties) { + return (ExpressionFactory) FactoryFinder.find( + "javax.el.ExpressionFactory", + "com.sun.el.ExpressionFactoryImpl", + properties); + } + + /** + * Parses an expression into a {@link ValueExpression} for later + * evaluation. Use this method for expressions that refer to values. + * + *

    This method should perform syntactic validation of the expression. + * If in doing so it detects errors, it should raise an + * ELException.

    + * + * @param context The EL context used to parse the expression. + * The FunctionMapper and VariableMapper + * stored in the ELContext + * are used to resolve functions and variables found in + * the expression. They can be null, in which case + * functions or variables are not supported for this expression. + * The object + * returned must invoke the same functions and access the same + * variable mappings + * regardless of whether + * the mappings in the provided FunctionMapper + * and VariableMapper instances + * change between calling + * ExpressionFactory.createValueExpression() and any + * method on ValueExpression. + *

    + * Note that within the EL, the ${} and #{} syntaxes are treated identically. + * This includes the use of VariableMapper and FunctionMapper at expression creation + * time. Each is invoked if not null, independent + * of whether the #{} or ${} syntax is used for the expression.

    + * @param expression The expression to parse + * @param expectedType The type the result of the expression + * will be coerced to after evaluation. + * @return The parsed expression + * @throws NullPointerException Thrown if expectedType is null. + * @throws ELException Thrown if there are syntactical errors in the + * provided expression. + */ + public abstract ValueExpression createValueExpression( + ELContext context, + String expression, + Class expectedType); + + /** + * Creates a ValueExpression that wraps an object instance. This + * method can be used to pass any object as a ValueExpression. The + * wrapper ValueExpression is read only, and returns the wrapped + * object via its getValue() method, optionally coerced. + * + * @param instance The object instance to be wrapped. + * @param expectedType The type the result of the expression + * will be coerced to after evaluation. There will be no + * coercion if it is Object.class, + * @throws NullPointerException Thrown if expectedType is null. + */ + public abstract ValueExpression createValueExpression( + Object instance, + Class expectedType); + + /** + * Parses an expression into a {@link MethodExpression} for later + * evaluation. Use this method for expressions that refer to methods. + * + *

    + * If the expression is a String literal, a MethodExpression + * is created, which when invoked, returns the String literal, + * coerced to expectedReturnType. An ELException is thrown if + * expectedReturnType is void or if the coercion of the String literal + * to the expectedReturnType yields an error (see Section "1.16 Type + * Conversion"). + *

    + *

    This method should perform syntactic validation of the expression. + * If in doing so it detects errors, it should raise an + * ELException.

    + * + * @param context The EL context used to parse the expression. + * The FunctionMapper and VariableMapper + * stored in the ELContext + * are used to resolve functions and variables found in + * the expression. They can be null, in which + * case functions or variables are not supported for this expression. + * The object + * returned must invoke the same functions and access the same variable + * mappings + * regardless of whether + * the mappings in the provided FunctionMapper + * and VariableMapper instances + * change between calling + * ExpressionFactory.createMethodExpression() and any + * method on MethodExpression. + *

    + * Note that within the EL, the ${} and #{} syntaxes are treated identically. + * This includes the use of VariableMapper and FunctionMapper at expression creation + * time. Each is invoked if not null, independent + * of whether the #{} or ${} syntax is used for the expression.

    + * + * @param expression The expression to parse + * @param expectedReturnType The expected return type for the method + * to be found. After evaluating the expression, the + * MethodExpression must check that the return type of + * the actual method matches this type. Passing in a value of + * null indicates the caller does not care what the + * return type is, and the check is disabled. + * @param expectedParamTypes The expected parameter types for the method to + * be found. Must be an array with no elements if there are + * no parameters expected. It is illegal to pass null, + * unless the method is specified with arugments in the EL + * expression, in which case these arguments are used for method + * selection, and this parameter is ignored. + * @return The parsed expression + * @throws ELException Thrown if there are syntactical errors in the + * provided expression. + * @throws NullPointerException if paramTypes is null. + */ + public abstract MethodExpression createMethodExpression( + ELContext context, + String expression, + Class expectedReturnType, + Class[] expectedParamTypes); + + /** + * Coerces an object to a specific type according to the + * EL type conversion rules. The custom type conversions in the + * ELResolvers are not considered. + * + *

    An ELException is thrown if an error results from + * applying the conversion rules. + *

    + * + * @param obj The object to coerce. + * @param targetType The target type for the coercion. + * @throws ELException thrown if an error results from applying the + * conversion rules. + */ + public abstract Object coerceToType( + Object obj, + Class targetType); + + /** + * Retrieves an ELResolver that implements the operations in collections. + * + *

    This ELResolver resolves the method invocation on the pair + * (base, property) when base is + * a Collection or a Map, and + * property is the name of the operation. + *

    See EL.2 for detailed descriptions of these operators, their + * arguments, and return values.

    + * + * @return The ELResolver that implements the Query Operators. + * + * @since EL 3.0 + */ + public ELResolver getStreamELResolver() { + return null; + } + + /** + * Retrieve a function map containing a pre-configured function + * mapping. It must include the following functions. + *
      + *
    • linq:range
    • + *
    • linq:repeat
    • + *
    • linq:_empty
    • + *
    + * @return A initial map for functions, null if there is none. + * + * @since EL 3.0 + */ + public Map getInitFunctionMap() { + return null; + } +} + + diff --git a/fine-third-default/fine-javax-el/src/javax/el/FactoryFinder.java b/fine-third-default/fine-javax-el/src/javax/el/FactoryFinder.java new file mode 100644 index 000000000..e4b7c87dc --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/FactoryFinder.java @@ -0,0 +1,200 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.lang.reflect.Constructor; +import java.io.InputStream; +import java.io.File; +import java.io.FileInputStream; +import java.util.Properties; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +class FactoryFinder { + + /** + * Creates an instance of the specified class using the specified + * ClassLoader object. + * + * @exception ELException if the given class could not be found + * or could not be instantiated + */ + private static Object newInstance(String className, + ClassLoader classLoader, + Properties properties) + { + try { + Class spiClass; + if (classLoader == null) { + spiClass = Class.forName(className); + } else { + spiClass = classLoader.loadClass(className); + } + if (properties != null) { + Constructor constr = null; + try { + constr = spiClass.getConstructor(Properties.class); + } catch (Exception ex) { + } + if (constr != null) { + return constr.newInstance(properties); + } + } + return spiClass.newInstance(); + } catch (ClassNotFoundException x) { + throw new ELException( + "Provider " + className + " not found", x); + } catch (Exception x) { + throw new ELException( + "Provider " + className + " could not be instantiated: " + x, + x); + } + } + + /** + * Finds the implementation Class object for the given + * factory name, or if that fails, finds the Class object + * for the given fallback class name. The arguments supplied must be + * used in order. If using the first argument is successful, the second + * one will not be used. + *

    + * This method is package private so that this code can be shared. + * + * @return the Class object of the specified message factory; + * may not be null + * + * @param factoryId the name of the factory to find, which is + * a system property + * @param fallbackClassName the implementation class name, which is + * to be used only if nothing else + * is found; null to indicate that + * there is no fallback class name + * @exception ELException if there is an error + */ + static Object find(String factoryId, String fallbackClassName, + Properties properties) + { + ClassLoader classLoader; + try { + classLoader = Thread.currentThread().getContextClassLoader(); + } catch (Exception x) { + throw new ELException(x.toString(), x); + } + + String serviceId = "META-INF/services/" + factoryId; + // try to find services in CLASSPATH + try { + InputStream is=null; + if (classLoader == null) { + is=ClassLoader.getSystemResourceAsStream(serviceId); + } else { + is=classLoader.getResourceAsStream(serviceId); + } + + if( is!=null ) { + BufferedReader rd = + new BufferedReader(new InputStreamReader(is, "UTF-8")); + + String factoryClassName = rd.readLine(); + rd.close(); + + if (factoryClassName != null && + ! "".equals(factoryClassName)) { + return newInstance(factoryClassName, classLoader, properties); + } + } + } catch( Exception ex ) { + } + + + // try to read from $java.home/lib/el.properties + try { + String javah=System.getProperty( "java.home" ); + String configFile = javah + File.separator + + "lib" + File.separator + "el.properties"; + File f=new File( configFile ); + if( f.exists()) { + Properties props=new Properties(); + props.load( new FileInputStream(f)); + String factoryClassName = props.getProperty(factoryId); + return newInstance(factoryClassName, classLoader, properties); + } + } catch(Exception ex ) { + } + + + // Use the system property + try { + String systemProp = + System.getProperty( factoryId ); + if( systemProp!=null) { + return newInstance(systemProp, classLoader, properties); + } + } catch (SecurityException se) { + } + + if (fallbackClassName == null) { + throw new ELException( + "Provider for " + factoryId + " cannot be found", null); + } + + return newInstance(fallbackClassName, classLoader, properties); + } +} + diff --git a/fine-third-default/fine-javax-el/src/javax/el/FunctionMapper.java b/fine-third-default/fine-javax-el/src/javax/el/FunctionMapper.java new file mode 100644 index 000000000..937274bab --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/FunctionMapper.java @@ -0,0 +1,107 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.lang.reflect.Method; + +/** + * The interface to a map between EL function names and methods. + * + *

    A FunctionMapper maps ${prefix:name()} + * style functions to a static method that can execute that function.

    + * + * @since JSP 2.1 + */ +public abstract class FunctionMapper { + + /** + * Resolves the specified prefix and local name into a + * java.lang.Method. + * + *

    Returns null if no function could be found that matches + * the given prefix and local name.

    + * + * @param prefix the prefix of the function, or "" if no prefix. + * For example, "fn" in ${fn:method()}, or + * "" in ${method()}. + * @param localName the short name of the function. For example, + * "method" in ${fn:method()}. + * @return the static method to invoke, or null if no + * match was found. + */ + public abstract Method resolveFunction(String prefix, + String localName); + + + /** + * Adds a static method that can be used as a function. + * @param prefix the prefix of the function, or "" if no prefix. + * For example, "fn" in ${fn:method()}, or + * "" in ${method()}. + * @param localName the short name of the function. For example, + * "method" in ${fn:method()}. + * @param meth The static method that is to be invoked, when the function is + * referenced. The null value causes the function to be removed from the + * map. + * + * @since EL 3.0 + */ + public void mapFunction(String prefix, String localName, Method meth) { + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ImportHandler.java b/fine-third-default/fine-javax-el/src/javax/el/ImportHandler.java new file mode 100644 index 000000000..fb279a278 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ImportHandler.java @@ -0,0 +1,190 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.el; + +import java.util.Map; +import java.util.List; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.HashSet; +import java.lang.reflect.Modifier; + +/** + * Handles imports of class names and package names. An imported package + * name implicitly imports all the classes in the package. A class that has + * been imported can be used without its package name. + * The name is resolved to its full (package and class) name + * at evaluation time. + */ +public class ImportHandler { + + private Map classNameMap = new HashMap(); + private Map> classMap = new HashMap>(); + private Map staticNameMap = new HashMap(); + private HashSet notAClass = new HashSet(); + private List packages = new ArrayList(); + + { + importPackage("java.lang"); + } + + /** + * Import a static field or method. + * @param name The static member name, including the full class name, + * to be imported + * @throws ELException if the name does not include a ".". + */ + public void importStatic(String name) throws ELException { + int i = name.lastIndexOf('.'); + if (i <= 0) { + throw new ELException( + "The name " + name + " is not a full static member name"); + } + String memberName = name.substring(i+1); + String className = name.substring(0, i); + staticNameMap.put(memberName, className); + } + + /** + * Import a class. + * @param name The full class name of the class to be imported + * @throws ELException if the name does not include a ".". + */ + public void importClass(String name) throws ELException { + int i = name.lastIndexOf('.'); + if (i <= 0) { + throw new ELException( + "The name " + name + " is not a full class name"); + } + String className = name.substring(i+1); + classNameMap.put(className, name); + } + + /** + * Import all the classes in a package. + * @param packageName The package name to be imported + */ + public void importPackage(String packageName) { + packages.add(packageName); + } + + /** + * Resolve a class name. + * + * @param name The name of the class (without package name) to be resolved. + * @return If the class has been imported previously, with + * {@link #importClass} or {@link #importPackage}, then its + * Class instance. Otherwise null. + * @throws ELException if the class is abstract or is an interface, or + * not public. + */ + public Class resolveClass(String name) { + + String className = classNameMap.get(name); + if (className != null) { + return resolveClassFor(className); + } + + for (String packageName: packages) { + String fullClassName = packageName + "." + name; + Classc = resolveClassFor(fullClassName); + if (c != null) { + classNameMap.put(name, fullClassName); + return c; + } + } + return null; + } + + /** + * Resolve a static field or method name. + * + * @param name The name of the member(without package and class name) + * to be resolved. + * @return If the field or method has been imported previously, with + * {@link #importStatic}, then the class object representing the class that + * declares the static field or method. + * Otherwise null. + * @throws ELException if the class is not public, or is abstract or + * is an interface. + */ + public Class resolveStatic(String name) { + String className = staticNameMap.get(name); + if (className != null) { + Class c = resolveClassFor(className); + if (c != null) { + return c; + } + } + return null; + } + + private Class resolveClassFor(String className) { + Class c = classMap.get(className); + if (c != null) { + return c; + } + c = getClassFor(className); + if (c != null) { + checkModifiers(c.getModifiers()); + classMap.put(className, c); + } + return c; + } + + private Class getClassFor(String className) { + if (!notAClass.contains(className)) { + try { + return Class.forName(className, false, getClass().getClassLoader()); + } catch (ClassNotFoundException ex) { + notAClass.add(className); + } + } + return null; + } + + private void checkModifiers(int modifiers) { + if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers) + || ! Modifier.isPublic((modifiers))) { + throw new ELException("Imported class must be public, and cannot be abstract or an interface"); + } + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/LambdaExpression.java b/fine-third-default/fine-javax-el/src/javax/el/LambdaExpression.java new file mode 100644 index 000000000..81c79da61 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/LambdaExpression.java @@ -0,0 +1,182 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.el; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; + +/** + *

    Encapsulates a parameterized {@link ValueExpression}.

    + * + *

    A LambdaExpression is a representation of the EL Lambda + * expression syntax. It consists of a list of the formal parameters and a + * body, represented by a {@link ValueExpression}. + * The body can be any valid Expression, including another + * LambdaExpression.

    + * A LambdaExpression is created when an EL expression containing + * a Lambda expression is evaluated.

    + *

    A LambdaExpression can be invoked by calling + * {@link LambdaExpression#invoke}, with + * an {@link javax.el.ELContext} and a list of the actual arguments. + * Alternately, a LambdaExpression can be invoked without passing + * a ELContext, in which case the ELContext previously + * set by calling {@link LambdaExpression#setELContext} will be used. + * The evaluation of the ValueExpression in the body uses the + * {@link ELContext} to resolve references to the parameters, and to evaluate + * the lambda expression. + * The result of the evaluation is returned.

    + * @see ELContext#getLambdaArgument + * @see ELContext#enterLambdaScope + * @see ELContext#exitLambdaScope + */ + +public class LambdaExpression { + + private List formalParameters = new ArrayList(); + private ValueExpression expression; + private ELContext context; + // Arguments from nesting lambdas, when the body is another lambda + private Map envirArgs = null; + + /** + * Creates a new LambdaExpression. + * @param formalParameters The list of String representing the formal + * parameters. + * @param expression The ValueExpression representing the + * body. + */ + public LambdaExpression (List formalParameters, + ValueExpression expression) { + this.formalParameters = formalParameters; + this.expression = expression; + this.envirArgs = new HashMap(); + } + + /** + * Set the ELContext to use in evaluating the LambdaExpression. + * The ELContext must to be set prior to the invocation of the LambdaExpression, + * unless it is supplied with {@link LambdaExpression#invoke}. + * @param context The ELContext to use in evaluating the LambdaExpression. + */ + public void setELContext(ELContext context) { + this.context = context; + } + + /** + * Invoke the encapsulated Lambda expression. + *

    The supplied arguments are matched, in + * the same order, to the formal parameters. If there are more arguments + * than the formal parameters, the extra arguments are ignored. If there + * are less arguments than the formal parameters, an + * ELException is thrown.

    + * + *

    The actual Lambda arguments are added to the ELContext and are + * available during the evaluation of the Lambda expression. They are + * removed after the evaluation.

    + * + * @param elContext The ELContext used for the evaluation of the expression + * The ELContext set by {@link #setELContext} is ignored. + * @param args The arguments to invoke the Lambda expression. For calls with + * no arguments, an empty array must be provided. A Lambda argument + * can be null. + * @return The result of invoking the Lambda expression + * @throws ELException if not enough arguments are provided + * @throws NullPointerException is elContext is null + */ + public Object invoke(ELContext elContext, Object... args) + throws ELException { + int i = 0; + Map lambdaArgs = new HashMap(); + + // First get arguments injected from the outter lambda, if any + lambdaArgs.putAll(envirArgs); + + for (String fParam: formalParameters) { + if (i >= args.length) { + throw new ELException("Expected Argument " + fParam + + " missing in Lambda Expression"); + } + lambdaArgs.put(fParam, args[i++]); + } + + elContext.enterLambdaScope(lambdaArgs); + Object ret = expression.getValue(elContext); + + // If the result of evaluating the body is another LambdaExpression, + // whose body has not been evaluated yet. (A LambdaExpression is + // evaluated iff when its invoke method is called.) The current lambda + // arguments may be needed in that body when it is evaluated later, + // after the current lambda exits. To make these arguments available + // then, they are injected into it. + if (ret instanceof LambdaExpression) { + ((LambdaExpression) ret).envirArgs.putAll(lambdaArgs); + } + elContext.exitLambdaScope(); + return ret; + } + + /** + * Invoke the encapsulated Lambda expression. + *

    The supplied arguments are matched, in + * the same order, to the formal parameters. If there are more arguments + * than the formal parameters, the extra arguments are ignored. If there + * are less arguments than the formal parameters, an + * ELException is thrown.

    + * + *

    The actual Lambda arguments are added to the ELContext and are + * available during the evaluation of the Lambda expression. They are + * removed after the evaluation.

    + * + * The ELContext set by {@link LambdaExpression#setELContext} is used in + * the evaluation of the lambda Expression. + * + * @param args The arguments to invoke the Lambda expression. For calls with + * no arguments, an empty array must be provided. A Lambda argument + * can be null. + * @return The result of invoking the Lambda expression + * @throws ELException if not enough arguments are provided + */ + public Object invoke(Object... args) { + return invoke(this.context, args); + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ListELResolver.java b/fine-third-default/fine-javax-el/src/javax/el/ListELResolver.java new file mode 100644 index 000000000..8f3276b99 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ListELResolver.java @@ -0,0 +1,416 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.util.List; +import java.util.Iterator; +import java.util.Collections; +import java.util.ArrayList; +import java.beans.FeatureDescriptor; + + +/** + * Defines property resolution behavior on instances of {@link java.util.List}. + * + *

    This resolver handles base objects of type java.util.List. + * It accepts any object as a property and coerces that object into an + * integer index into the list. The resulting value is the value in the list + * at that index.

    + * + *

    This resolver can be constructed in read-only mode, which means that + * {@link #isReadOnly} will always return true and + * {@link #setValue} will always throw + * PropertyNotWritableException.

    + * + *

    ELResolvers are combined together using + * {@link CompositeELResolver}s, to define rich semantics for evaluating + * an expression. See the javadocs for {@link ELResolver} for details.

    + * + * @see CompositeELResolver + * @see ELResolver + * @see java.util.List + * @since JSP 2.1 + */ +public class ListELResolver extends ELResolver { + + /** + * Creates a new read/write ListELResolver. + */ + public ListELResolver() { + this.isReadOnly = false; + } + + /** + * Creates a new ListELResolver whose read-only status is + * determined by the given parameter. + * + * @param isReadOnly true if this resolver cannot modify + * lists; false otherwise. + */ + public ListELResolver(boolean isReadOnly) { + this.isReadOnly = isReadOnly; + } + + /** + * If the base object is a list, returns the most general acceptable type + * for a value in this list. + * + *

    If the base is a List, the propertyResolved + * property of the ELContext object must be set to + * true by this resolver, before returning. If this property + * is not true after this method is called, the caller + * should ignore the return value.

    + * + *

    Assuming the base is a List, this method will always + * return Object.class. This is because Lists + * accept any object as an element.

    + * + * @param context The context of this evaluation. + * @param base The list to analyze. Only bases of type List + * are handled by this resolver. + * @param property The index of the element in the list to return the + * acceptable type for. Will be coerced into an integer, but + * otherwise ignored by this resolver. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the most general acceptable type; otherwise undefined. + * @throws PropertyNotFoundException if the given index is out of + * bounds for this list. + * @throws NullPointerException if context is null + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public Class getType(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof List) { + context.setPropertyResolved(true); + List list = (List) base; + int index = toInteger(property); + if (index < 0 || index >= list.size()) { + throw new PropertyNotFoundException(); + } + return Object.class; + } + return null; + } + + /** + * If the base object is a list, returns the value at the given index. + * The index is specified by the property argument, and + * coerced into an integer. If the coercion could not be performed, + * an IllegalArgumentException is thrown. If the index is + * out of bounds, null is returned. + * + *

    If the base is a List, the propertyResolved + * property of the ELContext object must be set to + * true by this resolver, before returning. If this property + * is not true after this method is called, the caller + * should ignore the return value.

    + * + * @param context The context of this evaluation. + * @param base The list to be analyzed. Only bases of type + * List are handled by this resolver. + * @param property The index of the value to be returned. Will be coerced + * into an integer. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the value at the given index or null + * if the index was out of bounds. Otherwise, undefined. + * @throws IllegalArgumentException if the property could not be coerced + * into an integer. + * @throws NullPointerException if context is null. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public Object getValue(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof List) { + context.setPropertyResolved(base, property); + List list = (List) base; + int index = toInteger(property); + if (index < 0 || index >= list.size()) { + return null; + } + return list.get(index); + } + return null; + } + + /** + * If the base object is a list, attempts to set the value at the + * given index with the given value. The index is specified by the + * property argument, and coerced into an integer. If the + * coercion could not be performed, an + * IllegalArgumentException is thrown. If the index is + * out of bounds, a PropertyNotFoundException is thrown. + * + *

    If the base is a List, the propertyResolved + * property of the ELContext object must be set to + * true by this resolver, before returning. If this property + * is not true after this method is called, the caller + * can safely assume no value was set.

    + * + *

    If this resolver was constructed in read-only mode, this method will + * always throw PropertyNotWritableException.

    + * + *

    If a List was created using + * {@link java.util.Collections#unmodifiableList}, this method must + * throw PropertyNotWritableException. Unfortunately, + * there is no Collections API method to detect this. However, an + * implementation can create a prototype unmodifiable List + * and query its runtime type to see if it matches the runtime type of + * the base object as a workaround.

    + * + * @param context The context of this evaluation. + * @param base The list to be modified. Only bases of type + * List are handled by this resolver. + * @param property The index of the value to be set. Will be coerced + * into an integer. + * @param val The value to be set at the given index. + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this list. + * @throws NullPointerException if context is null, or + * if the value is null and this List + * does not support null elements. + * @throws IllegalArgumentException if the property could not be coerced + * into an integer, or if some aspect of the specified element + * prevents it from being added to this list. + * @throws PropertyNotWritableException if this resolver was constructed + * in read-only mode, or if the set operation is not supported by + * the underlying list. + * @throws PropertyNotFoundException if the given index is out of + * bounds for this list. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public void setValue(ELContext context, + Object base, + Object property, + Object val) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof List) { + context.setPropertyResolved(base, property); + // Safe cast + @SuppressWarnings("unchecked") + List list = (List) base; + int index = toInteger(property); + if (isReadOnly) { + throw new PropertyNotWritableException(); + } + try { + list.set(index, val); + } catch (UnsupportedOperationException ex) { + throw new PropertyNotWritableException(); + } catch (IndexOutOfBoundsException ex) { + throw new PropertyNotFoundException(); + } catch (ClassCastException ex) { + throw ex; + } catch (NullPointerException ex) { + throw ex; + } catch (IllegalArgumentException ex) { + throw ex; + } + } + } + + static private Class theUnmodifiableListClass = + Collections.unmodifiableList(new ArrayList()).getClass(); + + /** + * If the base object is a list, returns whether a call to + * {@link #setValue} will always fail. + * + *

    If the base is a List, the propertyResolved + * property of the ELContext object must be set to + * true by this resolver, before returning. If this property + * is not true after this method is called, the caller + * should ignore the return value.

    + * + *

    If this resolver was constructed in read-only mode, this method will + * always return true.

    + * + *

    If a List was created using + * {@link java.util.Collections#unmodifiableList}, this method must + * return true. Unfortunately, there is no Collections API + * method to detect this. However, an implementation can create a + * prototype unmodifiable List and query its runtime type + * to see if it matches the runtime type of the base object as a + * workaround.

    + * + * @param context The context of this evaluation. + * @param base The list to analyze. Only bases of type List + * are handled by this resolver. + * @param property The index of the element in the list to return the + * acceptable type for. Will be coerced into an integer, but + * otherwise ignored by this resolver. + * @return If the propertyResolved property of + * ELContext was set to true, then + * true if calling the setValue method + * will always fail or false if it is possible that + * such a call may succeed; otherwise undefined. + * @throws PropertyNotFoundException if the given index is out of + * bounds for this list. + * @throws NullPointerException if context is null + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public boolean isReadOnly(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof List) { + context.setPropertyResolved(true); + List list = (List) base; + int index = toInteger(property); + if (index < 0 || index >= list.size()) { + throw new PropertyNotFoundException(); + } + return list.getClass() == theUnmodifiableListClass || isReadOnly; + } + return false; + } + + /** + * Always returns null, since there is no reason to + * iterate through set set of all integers. + * + *

    The {@link #getCommonPropertyType} method returns sufficient + * information about what properties this resolver accepts.

    + * + * @param context The context of this evaluation. + * @param base The list. Only bases of type List are + * handled by this resolver. + * @return null. + */ + public Iterator getFeatureDescriptors( + ELContext context, + Object base) { + return null; + } + + /** + * If the base object is a list, returns the most general type that + * this resolver accepts for the property argument. + * Otherwise, returns null. + * + *

    Assuming the base is a List, this method will always + * return Integer.class. This is because Lists + * accept integers as their index.

    + * + * @param context The context of this evaluation. + * @param base The list to analyze. Only bases of type List + * are handled by this resolver. + * @return null if base is not a List; otherwise + * Integer.class. + */ + public Class getCommonPropertyType(ELContext context, + Object base) { + if (base != null && base instanceof List) { + return Integer.class; + } + return null; + } + + private int toInteger(Object p) { + if (p instanceof Integer) { + return ((Integer) p).intValue(); + } + if (p instanceof Character) { + return ((Character) p).charValue(); + } + if (p instanceof Number) { + return ((Number) p).intValue(); + } + if (p instanceof String) { + return Integer.parseInt((String) p); + } + throw new IllegalArgumentException(); + } + + private boolean isReadOnly; +} + diff --git a/fine-third-default/fine-javax-el/src/javax/el/MapELResolver.java b/fine-third-default/fine-javax-el/src/javax/el/MapELResolver.java new file mode 100644 index 000000000..86074cd39 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/MapELResolver.java @@ -0,0 +1,418 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.beans.FeatureDescriptor; +import java.util.Map; +import java.util.Iterator; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; + +/** + * Defines property resolution behavior on instances of {@link java.util.Map}. + * + *

    This resolver handles base objects of type java.util.Map. + * It accepts any object as a property and uses that object as a key in + * the map. The resulting value is the value in the map that is associated with + * that key.

    + * + *

    This resolver can be constructed in read-only mode, which means that + * {@link #isReadOnly} will always return true and + * {@link #setValue} will always throw + * PropertyNotWritableException.

    + * + *

    ELResolvers are combined together using + * {@link CompositeELResolver}s, to define rich semantics for evaluating + * an expression. See the javadocs for {@link ELResolver} for details.

    + * + * @see CompositeELResolver + * @see ELResolver + * @see java.util.Map + * @since JSP 2.1 + */ +public class MapELResolver extends ELResolver { + + /** + * Creates a new read/write MapELResolver. + */ + public MapELResolver() { + this.isReadOnly = false; + } + + /** + * Creates a new MapELResolver whose read-only status is + * determined by the given parameter. + * + * @param isReadOnly true if this resolver cannot modify + * maps; false otherwise. + */ + public MapELResolver(boolean isReadOnly) { + this.isReadOnly = isReadOnly; + } + + /** + * If the base object is a map, returns the most general acceptable type + * for a value in this map. + * + *

    If the base is a Map, the propertyResolved + * property of the ELContext object must be set to + * true by this resolver, before returning. If this property + * is not true after this method is called, the caller + * should ignore the return value.

    + * + *

    Assuming the base is a Map, this method will always + * return Object.class. This is because Maps + * accept any object as the value for a given key.

    + * + * @param context The context of this evaluation. + * @param base The map to analyze. Only bases of type Map + * are handled by this resolver. + * @param property The key to return the acceptable type for. + * Ignored by this resolver. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the most general acceptable type; otherwise undefined. + * @throws NullPointerException if context is null + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public Class getType(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof Map) { + context.setPropertyResolved(true); + return Object.class; + } + return null; + } + + /** + * If the base object is a map, returns the value associated with the + * given key, as specified by the property argument. If the + * key was not found, null is returned. + * + *

    If the base is a Map, the propertyResolved + * property of the ELContext object must be set to + * true by this resolver, before returning. If this property + * is not true after this method is called, the caller + * should ignore the return value.

    + * + *

    Just as in {@link java.util.Map#get}, just because null + * is returned doesn't mean there is no mapping for the key; it's also + * possible that the Map explicitly maps the key to + * null.

    + * + * @param context The context of this evaluation. + * @param base The map to be analyzed. Only bases of type Map + * are handled by this resolver. + * @param property The key whose associated value is to be returned. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the value associated with the given key or null + * if the key was not found. Otherwise, undefined. + * @throws ClassCastException if the key is of an inappropriate type + * for this map (optionally thrown by the underlying Map). + * @throws NullPointerException if context is null, or if + * the key is null and this map does not permit null keys (the + * latter is optionally thrown by the underlying Map). + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public Object getValue(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof Map) { + context.setPropertyResolved(base, property); + Map map = (Map) base; + return map.get(property); + } + return null; + } + + static private Class theUnmodifiableMapClass = + Collections.unmodifiableMap(new HashMap()).getClass(); + + /** + * If the base object is a map, attempts to set the value associated with + * the given key, as specified by the property argument. + * + *

    If the base is a Map, the propertyResolved + * property of the ELContext object must be set to + * true by this resolver, before returning. If this property + * is not true after this method is called, the caller + * can safely assume no value was set.

    + * + *

    If this resolver was constructed in read-only mode, this method will + * always throw PropertyNotWritableException.

    + * + *

    If a Map was created using + * {@link java.util.Collections#unmodifiableMap}, this method must + * throw PropertyNotWritableException. Unfortunately, + * there is no Collections API method to detect this. However, an + * implementation can create a prototype unmodifiable Map + * and query its runtime type to see if it matches the runtime type of + * the base object as a workaround.

    + * + * @param context The context of this evaluation. + * @param base The map to be modified. Only bases of type Map + * are handled by this resolver. + * @param property The key with which the specified value is to be + * associated. + * @param val The value to be associated with the specified key. + * @throws ClassCastException if the class of the specified key or + * value prevents it from being stored in this map. + * @throws NullPointerException if context is null, or if + * this map does not permit null keys or values, and + * the specified key or value is null. + * @throws IllegalArgumentException if some aspect of this key or + * value prevents it from being stored in this map. + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + * @throws PropertyNotWritableException if this resolver was constructed + * in read-only mode, or if the put operation is not supported by + * the underlying map. + */ + public void setValue(ELContext context, + Object base, + Object property, + Object val) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof Map) { + context.setPropertyResolved(base, property); + // The cast is safe + @SuppressWarnings("unchecked") + Map map = (Map)base; + if (isReadOnly || map.getClass() == theUnmodifiableMapClass) { + throw new PropertyNotWritableException(); + } + try { + map.put(property, val); + } catch (UnsupportedOperationException ex) { + throw new PropertyNotWritableException(); + } + } + } + + /** + * If the base object is a map, returns whether a call to + * {@link #setValue} will always fail. + * + *

    If the base is a Map, the propertyResolved + * property of the ELContext object must be set to + * true by this resolver, before returning. If this property + * is not true after this method is called, the caller + * should ignore the return value.

    + * + *

    If this resolver was constructed in read-only mode, this method will + * always return true.

    + * + *

    If a Map was created using + * {@link java.util.Collections#unmodifiableMap}, this method must + * return true. Unfortunately, there is no Collections API + * method to detect this. However, an implementation can create a + * prototype unmodifiable Map and query its runtime type + * to see if it matches the runtime type of the base object as a + * workaround.

    + * + * @param context The context of this evaluation. + * @param base The map to analyze. Only bases of type Map + * are handled by this resolver. + * @param property The key to return the read-only status for. + * Ignored by this resolver. + * @return If the propertyResolved property of + * ELContext was set to true, then + * true if calling the setValue method + * will always fail or false if it is possible that + * such a call may succeed; otherwise undefined. + * @throws NullPointerException if context is null + * @throws ELException if an exception was thrown while performing + * the property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public boolean isReadOnly(ELContext context, + Object base, + Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof Map) { + context.setPropertyResolved(true); + Map map = (Map) base; + return isReadOnly || map.getClass() == theUnmodifiableMapClass; + } + return false; + } + + /** + * If the base object is a map, returns an Iterator + * containing the set of keys available in the Map. + * Otherwise, returns null. + * + *

    The Iterator returned must contain zero or more + * instances of {@link java.beans.FeatureDescriptor}. Each info object + * contains information about a key in the Map, and is initialized as + * follows: + *

    + *
  • displayName - The return value of calling the + * toString method on this key, or + * "null" if the key is null.
  • + *
  • name - Same as displayName property.
  • + *
  • shortDescription - Empty string
  • + *
  • expert - false
  • + *
  • hidden - false
  • + *
  • preferred - true
  • + *
    + * In addition, the following named attributes must be set in the + * returned FeatureDescriptors: + *
    + *
  • {@link ELResolver#TYPE} - The return value of calling the getClass() + * method on this key, or null if the key is + * null.
  • + *
  • {@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - true
  • + *
    + *

    + * + * @param context The context of this evaluation. + * @param base The map whose keys are to be iterated over. Only bases + * of type Map are handled by this resolver. + * @return An Iterator containing zero or more (possibly + * infinitely more) FeatureDescriptor objects, each + * representing a key in this map, or null if + * the base object is not a map. + */ + public Iterator getFeatureDescriptors( + ELContext context, + Object base) { + + if (base != null && base instanceof Map) { + Map map = (Map) base; + Iterator iter = map.keySet().iterator(); + List list = new ArrayList(); + while (iter.hasNext()) { + Object key = iter.next(); + FeatureDescriptor descriptor = new FeatureDescriptor(); + String name = (key==null)? null: key.toString(); + descriptor.setName(name); + descriptor.setDisplayName(name); + descriptor.setShortDescription(""); + descriptor.setExpert(false); + descriptor.setHidden(false); + descriptor.setPreferred(true); + if (key != null) { + descriptor.setValue("type", key.getClass()); + } + descriptor.setValue("resolvableAtDesignTime", Boolean.TRUE); + list.add(descriptor); + } + return list.iterator(); + } + return null; + } + + /** + * If the base object is a map, returns the most general type that + * this resolver accepts for the property argument. + * Otherwise, returns null. + * + *

    Assuming the base is a Map, this method will always + * return Object.class. This is because Maps + * accept any object as a key.

    + * + * @param context The context of this evaluation. + * @param base The map to analyze. Only bases of type Map + * are handled by this resolver. + * @return null if base is not a Map; otherwise + * Object.class. + */ + public Class getCommonPropertyType(ELContext context, + Object base) { + if (base != null && base instanceof Map) { + return Object.class; + } + return null; + } + + private boolean isReadOnly; +} + diff --git a/fine-third-default/fine-javax-el/src/javax/el/MethodExpression.java b/fine-third-default/fine-javax-el/src/javax/el/MethodExpression.java new file mode 100644 index 000000000..5f2408d14 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/MethodExpression.java @@ -0,0 +1,182 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +/** + * An Expression that refers to a method on an object. + * + *

    The {@link javax.el.ExpressionFactory#createMethodExpression} method + * can be used to parse an expression string and return a concrete instance + * of MethodExpression that encapsulates the parsed expression. + * The {@link FunctionMapper} is used at parse time, not evaluation time, + * so one is not needed to evaluate an expression using this class. + * However, the {@link ELContext} is needed at evaluation time.

    + * + *

    The {@link #getMethodInfo} and {@link #invoke} methods will evaluate the + * expression each time they are called. The {@link ELResolver} in the + * ELContext is used to resolve the top-level variables and to + * determine the behavior of the . and [] + * operators. For any of the two methods, the + * {@link javax.el.ELResolver#getValue} + * method is used to resolve all properties up to but excluding the last + * one. This provides the base object on which the method + * appears. If the base object is null, a + * PropertyNotFoundException must be thrown. + * At the last resolution, + * the final property is then coerced to a String, + * which provides the name of the method to be found. A method matching the + * name and expected parameters provided at parse time is found and it is + * either queried or invoked (depending on the method called on this + * MethodExpression).

    + * + *

    See the notes about comparison, serialization and immutability in + * the {@link Expression} javadocs. + * + * @see ELResolver + * @see Expression + * @see ExpressionFactory + * @since JSP 2.1 + */ +public abstract class MethodExpression extends Expression +{ + // Evaluation + + /** + * Evaluates the expression relative to the provided context, and + * returns information about the actual referenced method. + * + * @param context The context of this evaluation + * @return an instance of MethodInfo containing information + * about the method the expression evaluated to. + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if one of the property + * resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if an exception was thrown while performing + * property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public abstract MethodInfo getMethodInfo(ELContext context); + + /** + * If a String literal is specified as the expression, returns the + * String literal coerced to the expected return type of the method + * signature. An ELException is thrown if + * expectedReturnType is void or if the coercion of the String literal + * to the expectedReturnType yields an error (see Section "1.18 Type + * Conversion" of the EL specification). + * + * If not a String literal, evaluates the expression + * relative to the provided context, invokes the method that was + * found using the supplied parameters, and returns the result of + * the method invocation. + * + * Any parameters passed to this method is ignored if isLiteralText() + * or isParmetersProvided() is true. + * + * @param context The context of this evaluation. + * @param params The parameters to pass to the method, or + * null if no parameters. + * @return the result of the method invocation (null if + * the method has a void return type). + * @throws NullPointerException if context is null + * @throws PropertyNotFoundException if one of the property + * resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if a String literal is specified and + * expectedReturnType of the MethodExpression is void or if the coercion of the String literal + * to the expectedReturnType yields an error (see Section "1.18 Type + * Conversion"). + * @throws ELException if + * an exception was thrown while performing + * property or variable resolution. The thrown exception must be + * included as the cause property of this exception, if + * available. If the exception thrown is an + * InvocationTargetException, extract its + * cause and pass it to the + * ELException constructor. + */ + public abstract Object invoke(ELContext context, Object[] params); + + /** + * Return whether this MethodExpression was created with parameters. + * + *

    This method must return true if and only if + * parameters are specified in the EL, using the + * expr-a.expr-b(...) syntax.

    + * + * @return true if the MethodExpression was created with + * parameters, false otherwise. + * @since EL 2.2 + */ + public boolean isParametersProvided() { + return false; + } + + /** + * Use isParametersProvided instead. + */ + @Deprecated + public boolean isParmetersProvided() { + return isParametersProvided(); + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/MethodInfo.java b/fine-third-default/fine-javax-el/src/javax/el/MethodInfo.java new file mode 100644 index 000000000..328c26229 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/MethodInfo.java @@ -0,0 +1,113 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +/** + * Holds information about a method that a {@link MethodExpression} + * evaluated to. + * + * @since JSP 2.1 + */ +public class MethodInfo { + + /** + * Creates a new instance of MethodInfo with the given + * information. + * + * @param name The name of the method + * @param returnType The return type of the method + * @param paramTypes The types of each of the method's parameters + */ + public MethodInfo(String name, Class returnType, Class[] paramTypes) { + this.name = name; + this.returnType = returnType; + this.paramTypes = paramTypes; + } + + /** + * Returns the name of the method + * + * @return the name of the method + */ + public String getName() { + return this.name; + } + + /** + * Returns the return type of the method + * + * @return the return type of the method + */ + public Class getReturnType() { + return this.returnType; + } + + /** + * Returns the parameter types of the method + * + * @return the parameter types of the method + */ + public Class[] getParamTypes() { + return this.paramTypes; + } + + private String name; + private Class returnType; + private Class[] paramTypes; +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/MethodNotFoundException.java b/fine-third-default/fine-javax-el/src/javax/el/MethodNotFoundException.java new file mode 100644 index 000000000..9b724d980 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/MethodNotFoundException.java @@ -0,0 +1,107 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +/** + * Thrown when a method could not be found while evaluating a + * {@link MethodExpression}. + * + * @see MethodExpression + * @since JSP 2.1 + */ +public class MethodNotFoundException extends ELException { + + /** + * Creates a MethodNotFoundException with no detail message. + */ + public MethodNotFoundException() { + super (); + } + + /** + * Creates a MethodNotFoundException with the provided + * detail message. + * + * @param message the detail message + */ + public MethodNotFoundException(String message) { + super (message); + } + + /** + * Creates a MethodNotFoundException with the given root + * cause. + * + * @param exception the originating cause of this exception + */ + public MethodNotFoundException(Throwable exception) { + super (exception); + } + + /** + * Creates a MethodNotFoundException with the given detail + * message and root cause. + * + * @param pMessage the detail message + * @param pRootCause the originating cause of this exception + */ + public MethodNotFoundException(String pMessage, Throwable pRootCause) { + super (pMessage, pRootCause); + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/PrivateMessages.properties b/fine-third-default/fine-javax-el/src/javax/el/PrivateMessages.properties new file mode 100644 index 000000000..a22e84ad1 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/PrivateMessages.properties @@ -0,0 +1,68 @@ +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common Development +# and Distribution License("CDDL") (collectively, the "License"). You +# may not use this file except in compliance with the License. You can +# obtain a copy of the License at +# https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html +# or packager/legal/LICENSE.txt. See the License for the specific +# language governing permissions and limitations under the License. +# +# When distributing the software, include this License Header Notice in each +# file and include the License file at packager/legal/LICENSE.txt. +# +# GPL Classpath Exception: +# Oracle designates this particular file as subject to the "Classpath" +# exception as provided by Oracle in the GPL Version 2 section of the License +# file that accompanied this code. +# +# Modifications: +# If applicable, add the following below the License Header, with the fields +# enclosed by brackets [] replaced by your own identifying information: +# "Portions Copyright [year] [name of copyright owner]" +# +# Contributor(s): +# If you wish your version of this file to be governed by only the CDDL or +# only the GPL Version 2, indicate your decision by adding "[Contributor] +# elects to include this software in this distribution under the [CDDL or GPL +# Version 2] license." If you don't indicate a single choice of license, a +# recipient has the option to distribute your version of this file under +# either the CDDL, the GPL Version 2 or to extend the choice of license to +# its licensees as provided above. However, if you add GPL Version 2 code +# and therefore, elected the GPL Version 2 license, then the option applies +# only if the new code is made subject to such option by the copyright +# holder. +# +# +# This file incorporates work covered by the following copyright and +# permission notice: +# +# Copyright 2004 The Apache Software Foundation +# +# 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. +# + +# This properties file is essentially "package private" but because +# there is no way to attach an access specifier to a properties file we +# are including this comment to serve as such. +setPropertyFailed=Can''t set property ''{0}'' on class ''{1}'' to value ''{2}''. +propertyNotFound=The class ''{0}'' does not have the property ''{1}''. +propertyNotReadable=The class ''{0}'' does not have a readable property ''{1}''. +resolverNotWritable=The ELResolver for the class ''{0}'' is not writable. +propertyNotWritable=The class ''{0}'' does not have a writable property ''{1}''. +staticFieldReadError=Either ''{1}'' is not a public static field of the class ''{0}'' or field is inaccessible +staticFieldWriteError=Cannot write to the field ''{1}}'' of the class ''{0}'' diff --git a/fine-third-default/fine-javax-el/src/javax/el/PropertyNotFoundException.java b/fine-third-default/fine-javax-el/src/javax/el/PropertyNotFoundException.java new file mode 100644 index 000000000..9fb7bad75 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/PropertyNotFoundException.java @@ -0,0 +1,113 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +/** + * Thrown when a property could not be found while evaluating a + * {@link ValueExpression} or {@link MethodExpression}. + * + *

    For example, this could be triggered by an index out of bounds + * while setting an array value, or by an unreadable property while + * getting the value of a JavaBeans property.

    + * + * @since JSP 2.1 + */ +public class PropertyNotFoundException extends ELException { + + //------------------------------------- + /** + * Creates a PropertyNotFoundException with no detail message. + */ + public PropertyNotFoundException() { + super (); + } + + //------------------------------------- + /** + * Creates a PropertyNotFoundException with the provided + * detail message. + * + * @param message the detail message + */ + public PropertyNotFoundException(String message) { + super (message); + } + + /** + * Creates a PropertyNotFoundException with the given root + * cause. + * + * @param exception the originating cause of this exception + */ + public PropertyNotFoundException(Throwable exception) { + super (exception); + } + + /** + * Creates a PropertyNotFoundException with the given detail + * message and root cause. + * + * @param pMessage the detail message + * @param pRootCause the originating cause of this exception + */ + public PropertyNotFoundException(String pMessage, Throwable pRootCause) { + super (pMessage, pRootCause); + } + +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/PropertyNotWritableException.java b/fine-third-default/fine-javax-el/src/javax/el/PropertyNotWritableException.java new file mode 100644 index 000000000..5e77282f8 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/PropertyNotWritableException.java @@ -0,0 +1,116 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + + +/** + * Thrown when a property could not be written to while setting the + * value on a {@link ValueExpression}. + * + *

    For example, this could be triggered by trying to set a map value + * on an unmodifiable map.

    + * + * @since JSP 2.1 + */ +public class PropertyNotWritableException extends ELException { + + //------------------------------------- + /** + * Creates a PropertyNotWritableException with no detail + * message. + */ + public PropertyNotWritableException() { + super (); + } + + //------------------------------------- + /** + * Creates a PropertyNotWritableException with the + * provided detail message. + * + * @param pMessage the detail message + */ + public PropertyNotWritableException(String pMessage) { + super (pMessage); + } + + //------------------------------------- + /** + * Creates a PropertyNotWritableException with the given root + * cause. + * + * @param exception the originating cause of this exception + */ + public PropertyNotWritableException(Throwable exception) { + super (exception); + } + + //------------------------------------- + /** + * Creates a PropertyNotWritableException with the given + * detail message and root cause. + * + * @param pMessage the detail message + * @param pRootCause the originating cause of this exception + */ + public PropertyNotWritableException(String pMessage, Throwable pRootCause) { + super (pMessage, pRootCause); + } + +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ResourceBundleELResolver.java b/fine-third-default/fine-javax-el/src/javax/el/ResourceBundleELResolver.java new file mode 100644 index 000000000..77aa4bf76 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ResourceBundleELResolver.java @@ -0,0 +1,328 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +import java.beans.FeatureDescriptor; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Defines property resolution behavior on instances of + * {@link java.util.ResourceBundle}. + * + *

    + * This resolver handles base objects of type + * java.util.ResourceBundle. It accepts any object as a property + * and coerces it to a java.lang.String for invoking + * {@link java.util.ResourceBundle#getObject(java.lang.String)}. + *

    + * + *

    + * This resolver is read only and will throw a + * {@link PropertyNotWritableException} if setValue is called. + *

    + * + *

    + * ELResolvers are combined together using + * {@link CompositeELResolver}s, to define rich semantics for evaluating an + * expression. See the javadocs for {@link ELResolver} for details. + *

    + * + * @see CompositeELResolver + * @see ELResolver + * @see java.util.ResourceBundle + * @since JSP 2.1 + */ +public class ResourceBundleELResolver extends ELResolver { + + /** + * If the base object is an instance of ResourceBundle, + * the provided property will first be coerced to a String. + * The Object returned by getObject on + * the base ResourceBundle will be returned. + *

    + * If the base is ResourceBundle, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this method + * is called, the caller should ignore the return value. + *

    + * @param context + * The context of this evaluation. + * @param base + * The ResourceBundle to analyze. + * @param property + * The name of the property to analyze. Will be coerced to a + * String. + * @return If the propertyResolved property of + * ELContext was set to true, then + * null if property is null; + * otherwise the Object for the given key + * (property coerced to String) from the + * ResourceBundle. + * If no object for the given key can be found, then the + * String "???" + key + "???". + * @throws NullPointerException + * if context is null + * @throws ELException + * if an exception was thrown while performing the property or + * variable resolution. The thrown exception must be included as + * the cause property of this exception, if available. + */ + public Object getValue(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ResourceBundle) { + context.setPropertyResolved(true); + if (property != null) { + try { + return ((ResourceBundle) base).getObject(property + .toString()); + } catch (MissingResourceException e) { + return "???" + property + "???"; + } + } + } + return null; + } + + /** + * If the base object is an instance of ResourceBundle, + * return null, since the resolver is read only. + * + *

    + * If the base is ResourceBundle, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this method + * is called, the caller should ignore the return value. + *

    + * + * @param context + * The context of this evaluation. + * @param base + * The ResourceBundle to analyze. + * @param property + * The name of the property to analyze. + * @return If the propertyResolved property of + * ELContext was set to true, then + * null; otherwise undefined. + * @throws NullPointerException + * if context is null + */ + public Class getType(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ResourceBundle) { + context.setPropertyResolved(true); + } + return null; + } + + /** + * If the base object is a ResourceBundle, throw a + * {@link PropertyNotWritableException}. + * + * @param context + * The context of this evaluation. + * @param base + * The ResourceBundle to be modified. Only bases that are of type + * ResourceBundle are handled. + * @param property + * The String property to use. + * @param value + * The value to be set. + * @throws NullPointerException + * if context is null. + * @throws PropertyNotWritableException + * Always thrown if base is an instance of ReasourceBundle. + */ + public void setValue(ELContext context, Object base, Object property, + Object value) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ResourceBundle) { + context.setPropertyResolved(true); + throw new PropertyNotWritableException( + "ResourceBundles are immutable"); + } + } + + /** + * If the base object is not null and an instanceof {@link ResourceBundle}, + * return true. + * + * @param context + * The context of this evaluation. + * @param base + * The ResourceBundle to be modified. Only bases that are of type + * ResourceBundle are handled. + * @param property + * The String property to use. + * @return If the propertyResolved property of + * ELContext was set to true, then + * true; otherwise undefined. + * @throws NullPointerException + * if context is null + */ + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + if (base instanceof ResourceBundle) { + context.setPropertyResolved(true); + return true; + } + return false; + } + + /** + * If the base object is a ResourceBundle, returns an Iterator + * containing the set of keys available in the ResourceBundle. + * Otherwise, returns null. + * + *

    + * The Iterator returned must contain zero or more instances + * of {@link java.beans.FeatureDescriptor}. Each info object contains + * information about a key in the ResourceBundle, and is initialized as + * follows: + *

    + *
  • displayName - The String key + *
  • name - Same as displayName property.
  • + *
  • shortDescription - Empty string
  • + *
  • expert - false
  • + *
  • hidden - false
  • + *
  • preferred - true
  • + *
    + * In addition, the following named attributes must be set in the returned + * FeatureDescriptors: + *
    + *
  • {@link ELResolver#TYPE} - String.class
  • + *
  • {@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - true
  • + *
    + *

    + * + * @param context + * The context of this evaluation. + * @param base + * The bundle whose keys are to be iterated over. Only bases of + * type ResourceBundle are handled by this + * resolver. + * @return An Iterator containing zero or more (possibly + * infinitely more) FeatureDescriptor objects, each + * representing a key in this bundle, or null if the + * base object is not a ResourceBundle. + */ + public Iterator getFeatureDescriptors(ELContext context, Object base) { + if (base instanceof ResourceBundle) { + ResourceBundle bundle = (ResourceBundle) base; + List features = new ArrayList(); + String key = null; + FeatureDescriptor desc = null; + for (Enumeration e = bundle.getKeys(); e.hasMoreElements();) { + key = e.nextElement(); + desc = new FeatureDescriptor(); + desc.setDisplayName(key); + desc.setExpert(false); + desc.setHidden(false); + desc.setName(key); + desc.setPreferred(true); + desc.setValue(TYPE, String.class); + desc.setValue(RESOLVABLE_AT_DESIGN_TIME, Boolean.TRUE); + features.add(desc); + } + return features.iterator(); + } + return null; + } + + /** + * If the base object is a ResourceBundle, returns the most general type + * that this resolver accepts for the property argument. + * Otherwise, returns null. + * + *

    + * Assuming the base is a ResourceBundle, this method will + * always return String.class. + * + * @param context + * The context of this evaluation. + * @param base + * The bundle to analyze. Only bases of type + * ResourceBundle are handled by this resolver. + * @return null if base is not a ResourceBundle; + * otherwise String.class. + */ + public Class getCommonPropertyType(ELContext context, Object base) { + if (base instanceof ResourceBundle) { + return String.class; + } + return null; + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/StandardELContext.java b/fine-third-default/fine-javax-el/src/javax/el/StandardELContext.java new file mode 100644 index 000000000..9c81937a7 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/StandardELContext.java @@ -0,0 +1,308 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.el; + +import java.util.Map; +import java.util.HashMap; +import java.lang.reflect.Method; + +/** + * A standard ELContext suitable for use in a stand alone environment. + * This class provides a default implementation of an ELResolver that contains + * a number of useful ELResolvers. It also provides local repositories for + * the FunctionMapper, VariableMapper, and BeanNameResolver. + * + * @since EL 3.0 + */ + +public class StandardELContext extends ELContext { + + /* + * The ELResolver for this ELContext. + */ + private ELResolver elResolver; + + /* + * The list of the custom ELResolvers added to the ELResolvers. + * An ELResolver is added to the list when addELResolver is called. + */ + private CompositeELResolver customResolvers; + + /* + * The ELResolver implementing the query operators. + */ + private ELResolver streamELResolver; + + /* + * The FunctionMapper for this ELContext. + */ + private FunctionMapper functionMapper; + + /* + * The pre-confured init function map; + */ + private Map initFunctionMap; + + /* + * The VariableMapper for this ELContext. + */ + private VariableMapper variableMapper; + + /* + * If non-null, indicates the presence of a delegate ELContext. + * When a Standard is constructed from another ELContext, there is no + * easy way to get its private context map, therefore delegation is needed. + */ + private ELContext delegate = null; + + /** + * A bean repository local to this context + */ + private Map beans = new HashMap(); + + /** + * Construct a default ELContext for a stand-alone environment. + * @param factory The ExpressionFactory + */ + public StandardELContext(ExpressionFactory factory) { + this.streamELResolver = factory.getStreamELResolver(); + initFunctionMap = factory.getInitFunctionMap(); + } + + /** + * Construct a StandardELContext from another ELContext. + * @param context The ELContext that acts as a delegate in most cases + */ + public StandardELContext(ELContext context) { + this.delegate = context; + // Copy all attributes except map and resolved + CompositeELResolver elr = new CompositeELResolver(); + elr.add(new BeanNameELResolver(new LocalBeanNameResolver())); + customResolvers = new CompositeELResolver(); + elr.add(customResolvers); + elr.add(context.getELResolver()); + elResolver = elr; + + functionMapper = context.getFunctionMapper(); + variableMapper = context.getVariableMapper(); + setLocale(context.getLocale()); + } + + @Override + public void putContext(Class key, Object contextObject) { + if (delegate !=null) { + delegate.putContext(key, contextObject); + } else { + super.putContext(key, contextObject); + } + } + + @Override + public Object getContext(Class key) { + if (delegate !=null) { + return delegate.getContext(key); + } else { + return super.getContext(key); + } + } + + /** + * Construct (if needed) and return a default ELResolver. + *

    Retrieves the ELResolver associated with this context. + * This is a CompositeELResover consists of an ordered list of + * ELResolvers. + *

      + *
    1. A {@link BeanNameELResolver} for beans defined locally
    2. + *
    3. Any custom ELResolvers
    4. + *
    5. An ELResolver supporting the collection operations
    6. + *
    7. A {@link StaticFieldELResolver} for resolving static fields
    8. + *
    9. A {@link MapELResolver} for resolving Map properties
    10. + *
    11. A {@link ResourceBundleELResolver} for resolving ResourceBundle properties
    12. + *
    13. A {@link ListELResolver} for resolving List properties
    14. + *
    15. An {@link ArrayELResolver} for resolving array properties
    16. + *
    17. A {@link BeanELResolver} for resolving bean properties
    18. + *
    + *

    + * @return The ELResolver for this context. + */ + @Override + public ELResolver getELResolver() { + if (elResolver == null) { + CompositeELResolver resolver = new CompositeELResolver(); + resolver.add(new BeanNameELResolver(new LocalBeanNameResolver())); + customResolvers = new CompositeELResolver(); + resolver.add(customResolvers); + if (streamELResolver != null) { + resolver.add(streamELResolver); + } + resolver.add(new StaticFieldELResolver()); + resolver.add(new MapELResolver()); + resolver.add(new ResourceBundleELResolver()); + resolver.add(new ListELResolver()); + resolver.add(new ArrayELResolver()); + resolver.add(new BeanELResolver()); + elResolver = resolver; + } + return elResolver; + } + + /** + * Add a custom ELResolver to the context. The list of the custom + * ELResolvers will be accessed in the order they are added. + * A custom ELResolver added to the context cannot be removed. + * @param cELResolver The new ELResolver to be added to the context + */ + public void addELResolver(ELResolver cELResolver) { + getELResolver(); // make sure elResolver is constructed + customResolvers.add(cELResolver); + } + + /** + * Get the local bean repository + * @return the bean repository + */ + Map getBeans() { + return beans; + } + + /** + * Construct (if needed) and return a default FunctionMapper. + * @return The default FunctionMapper + */ + @Override + public FunctionMapper getFunctionMapper() { + if (functionMapper == null) { + functionMapper = new DefaultFunctionMapper(initFunctionMap); + } + return functionMapper; + } + + /** + * Construct (if needed) and return a default VariableMapper() { + * @return The default Variable + */ + @Override + public VariableMapper getVariableMapper() { + if (variableMapper == null) { + variableMapper = new DefaultVariableMapper(); + } + return variableMapper; + } + + private static class DefaultFunctionMapper extends FunctionMapper { + + private Map functions = null; + + DefaultFunctionMapper(Map initMap){ + functions = (initMap == null)? + new HashMap(): + new HashMap(initMap); + } + + @Override + public Method resolveFunction(String prefix, String localName) { + return functions.get(prefix + ":" + localName); + } + + + @Override + public void mapFunction(String prefix, String localName, Method meth){ + functions.put(prefix + ":" + localName, meth); + } + } + + private static class DefaultVariableMapper extends VariableMapper { + + private Map variables = null; + + @Override + public ValueExpression resolveVariable (String variable) { + if (variables == null) { + return null; + } + return variables.get(variable); + } + + @Override + public ValueExpression setVariable(String variable, + ValueExpression expression) { + if (variables == null) { + variables = new HashMap(); + } + ValueExpression prev = null; + if (expression == null) { + prev = variables.remove(variable); + } else { + prev = variables.put(variable, expression); + } + return prev; + } + } + + private class LocalBeanNameResolver extends BeanNameResolver { + + @Override + public boolean isNameResolved(String beanName) { + return beans.containsKey(beanName); + } + + @Override + public Object getBean(String beanName) { + return beans.get(beanName); + } + + @Override + public void setBeanValue(String beanName, Object value) { + beans.put(beanName, value); + } + + @Override + public boolean isReadOnly(String beanName) { + return false; + } + + @Override + public boolean canCreateBean(String beanName) { + return true; + } + } +} + diff --git a/fine-third-default/fine-javax-el/src/javax/el/StaticFieldELResolver.java b/fine-third-default/fine-javax-el/src/javax/el/StaticFieldELResolver.java new file mode 100644 index 000000000..b3d5a6de2 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/StaticFieldELResolver.java @@ -0,0 +1,318 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.el; + +import java.util.Iterator; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Constructor; + +import java.beans.FeatureDescriptor; + +/** + *

    An {@link ELResolver} for resolving static fields, enum constants and + * static methods. Also handles constructor calls as a special case.

    + *

    The resolver handles base objects of the type {@link ELClass}, which + * is usually generated by an EL implementation.

    + * + * @see ELClass + * @since EL 3.0 + */ +public class StaticFieldELResolver extends ELResolver { + + /** + *

    Returns the value of a static field.

    + *

    If the base object is an instance of ELClass and the + * property is String, the + * propertyResolved property of the ELContext + * object must be set to true by this resolver, before + * returning. If this property is not true after this + * method is called, the caller should ignore the return value.

    + * + * If the property is a public static field of class specified in + * ELClass, return the value of the static field. + * An Enum constant is a + * public static field of an Enum object, and is a special case of this. + * @param context The context of this evaluation. + * @param base An ELClass. + * @param property A static field name. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the static field value. + * @throws NullPointerException if context is null. + * @throws PropertyNotFoundException if the specified class does not exist, + * or if the field is not a public static filed of the class, + * or if the field is inaccessible. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ELClass && property instanceof String) { + Class klass = ((ELClass)base).getKlass(); + String fieldName = (String) property; + try { + context.setPropertyResolved(base, property); + Field field = klass.getField(fieldName); + int mod = field.getModifiers(); + if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) { + return field.get(null); + } + } catch (NoSuchFieldException ex) { + } catch (IllegalAccessException ex) { + } + throw new PropertyNotFoundException( + ELUtil.getExceptionMessageString(context, + "staticFieldReadError", + new Object[] { klass.getName(), fieldName})); + } + return null; + } + + /** + *

    Attempts to write to a static field.

    + *

    If the base object is an instance of ELClassand the + * property is String, a PropertyNotWritableException + * will always be thrown, because writing to a static field is not + * allowed. + * @param context The context of this evaluation. + * @param base An ELClass + * @param property The name of the field + * @param value The value to set the field of the class to. + * @throws NullPointerException if context is null + * @throws PropertyNotWritableException + */ + @Override + public void setValue(ELContext context, Object base, Object property, + Object value) { + if (context == null) { + throw new NullPointerException(); + } + if (base instanceof ELClass && property instanceof String) { + Class klass = ((ELClass)base).getKlass(); + String fieldName = (String) property; + throw new PropertyNotWritableException( + ELUtil.getExceptionMessageString(context, + "staticFieldWriteError", + new Object[] { klass.getName(), fieldName})); + } + } + + /** + *

    Invokes a public static method or the constructor for a class.

    + * + * If the base object is an instance of ELClass and the + * method is a String, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller + * should ignore the return value.

    + *

    Invoke the public static method specified by method.

    + *

    The process involved in the method selection is + * the same as that used in {@link BeanELResolver}.

    + * + *

    As a special case, if the name of the method is "<init>", the + * constructor for the class will be invoked.

    + * + * @param base An ELClass + * @param method When coerced to a String, + * the simple name of the method. + * @param paramTypes An array of Class objects identifying the + * method's formal parameter types, in declared order. + * Use an empty array if the method has no parameters. + * Can be null, in which case the method's formal + * parameter types are assumed to be unknown. + * @param params The parameters to pass to the method, or + * null if no parameters. + * @return The result of the method invocation (null if + * the method has a void return type). + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if an exception was thrown while performing + * (base, method) resolution. The thrown exception must be + * included as the cause property of this exception, if + * available. If the exception thrown is an + * InvocationTargetException, extract its + * cause and pass it to the + * ELException constructor. + */ + @Override + public Object invoke(ELContext context, + Object base, + Object method, + Class[] paramTypes, + Object[] params) { + + if (context == null) { + throw new NullPointerException(); + } + + if (!(base instanceof ELClass && method instanceof String)) { + return null; + } + + Class klass = ((ELClass)base).getKlass(); + String name = (String) method; + + Object ret; + if ("".equals(name)) { + Constructor constructor = + ELUtil.findConstructor(klass, paramTypes, params); + ret = ELUtil.invokeConstructor(context, constructor, params); + } else { + Method meth = + ELUtil.findMethod(klass, name, paramTypes, params, true); + ret = ELUtil.invokeMethod(context, meth, null, params); + } + context.setPropertyResolved(base, method); + return ret; + } + + /** + *

    Returns the type of a static field.

    + *

    If the base object is an instance of ELClassand the + * property is a String, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller can + * safely assume no value has been set.

    + * + * If the property string is a public static field of class specified in + * ELClass, return the type of the static field.

    + * @param context The context of this evaluation. + * @param base An ELClass. + * @param property The name of the field. + * @return If the propertyResolved property of + * ELContext was set to true, then + * the type of the type of the field. + * @throws NullPointerException if context is null. + * @throws PropertyNotFoundException if field is not a public static + * filed of the class, or if the field is inaccessible. + */ + @Override + public Class getType(ELContext context, Object base, Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ELClass && property instanceof String) { + Class klass = ((ELClass)base).getKlass(); + String fieldName = (String) property; + try { + context.setPropertyResolved(true); + Field field = klass.getField(fieldName); + int mod = field.getModifiers(); + if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) { + return field.getType(); + } + } catch (NoSuchFieldException ex) { + } + throw new PropertyNotFoundException( + ELUtil.getExceptionMessageString(context, + "staticFieldReadError", + new Object[] { klass.getName(), fieldName})); + } + return null; + } + + /** + *

    Inquires whether the static field is writable.

    + *

    If the base object is an instance of ELClassand the + * property is a String, + * the propertyResolved property of the + * ELContext object must be set to true + * by the resolver, before returning. If this property is not + * true after this method is called, the caller can + * safely assume no value has been set.

    + * + *

    Always returns a true because writing to a static field + * is not allowed.

    + * @param context The context of this evaluation. + * @param base An ELClass. + * @param property The name of the bean. + * @return true + * @throws NullPointerException if context is null. + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ELClass && property instanceof String) { + Class klass = ((ELClass)base).getKlass(); + context.setPropertyResolved(true); + } + return true; + } + + /** + * Returns the properties that can be resolved. + * Always returns null, since there is no reason to + * iterate through a list of one element: field name. + * @param context The context of this evaluation. + * @param base An ELClass. + * @return null. + */ + @Override + public Iterator getFeatureDescriptors( + ELContext context, Object base) { + return null; + } + + /** + * Returns the type of the property. + * Always returns String.class, since a field name is a String. + * @param context The context of this evaluation. + * @param base An ELClass. + * @return String.class. + */ + @Override + public Class getCommonPropertyType(ELContext context, Object base) { + return String.class; + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/TypeConverter.java b/fine-third-default/fine-javax-el/src/javax/el/TypeConverter.java new file mode 100644 index 000000000..231e80b55 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/TypeConverter.java @@ -0,0 +1,124 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.el; + +import java.util.Iterator; +import java.beans.FeatureDescriptor; + +/** + * A convenient class for writing an ELResolver to do custom type conversions. + * + *

    For example, to convert a String to an instance of MyDate, one can write + *

    + *     ELProcessor elp = new ELProcessor();
    + *     elp.getELManager().addELResolver(new TypeConverter() {
    + *         Object convertToType(ELContext context, Object obj, Class type) {
    + *             if ((obj instanceof String) && type == MyDate.class) {
    + *                 context.setPropertyResolved(obj, type);
    + *                 return (obj == null)? null: new MyDate(obj.toString());
    + *             }
    + *             return null;
    + *         }
    + *      };
    + * 
    + * + * @since EL 3.0 + */ + +public abstract class TypeConverter extends ELResolver { + + @Override + public Object getValue(ELContext context, + Object base, + Object property) { + return null; + } + + @Override + public Class getType(ELContext context, + Object base, + Object property) { + return null; + } + + @Override + public void setValue(ELContext context, + Object base, + Object property, + Object value) { + } + + @Override + public boolean isReadOnly(ELContext context, + Object base, + Object property){ + return false; + } + + @Override + public Iterator getFeatureDescriptors( + ELContext context, + Object base) { + return null; + } + + @Override + public Class getCommonPropertyType(ELContext context, + Object base) { + return null; + } + + /** + * Converts an object to a specific type. + * + *

    An ELException is thrown if an error occurs during + * the conversion.

    + * + * @param context The context of this evaluation. + * @param obj The object to convert. + * @param targetType The target type for the conversion. + * @throws ELException thrown if errors occur. + */ + @Override + abstract public Object convertToType(ELContext context, + Object obj, + Class targetType); +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ValueExpression.java b/fine-third-default/fine-javax-el/src/javax/el/ValueExpression.java new file mode 100644 index 000000000..892b1cb72 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ValueExpression.java @@ -0,0 +1,226 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +/** + * An Expression that can get or set a value. + * + *

    In previous incarnations of this API, expressions could only be + * read. ValueExpression objects can now be used both to + * retrieve a value and to set a value. Expressions that can have a value + * set on them are referred to as l-value expressions. Those that + * cannot are referred to as r-value expressions. Not all r-value expressions + * can be used as l-value expressions (e.g. "${1+1}" or + * "${firstName} ${lastName}"). See the EL Specification for + * details. Expressions that cannot be used as l-values must always + * return true from isReadOnly().

    + * + *

    The {@link ExpressionFactory#createValueExpression} method + * can be used to parse an expression string and return a concrete instance + * of ValueExpression that encapsulates the parsed expression. + * The {@link FunctionMapper} is used at parse time, not evaluation time, + * so one is not needed to evaluate an expression using this class. + * However, the {@link ELContext} is needed at evaluation time.

    + * + *

    The {@link #getValue}, {@link #setValue}, {@link #isReadOnly}, + * {@link #getType} and {@link #getValueReference} + * methods will evaluate the expression each time they are + * called. The {@link ELResolver} in the ELContext is used to + * resolve the top-level variables and to determine the behavior of the + * . and [] operators. For any of the five methods, + * the {@link ELResolver#getValue} method is used to resolve all properties + * up to but excluding the last one. This provides the base + * object. For all methods other than the {@link #getValueReference} method, + * at the last resolution, the ValueExpression will + * call the corresponding {@link ELResolver#getValue}, + * {@link ELResolver#setValue}, {@link ELResolver#isReadOnly} or + * {@link ELResolver#getType} method, depending on which was called on + * the ValueExpression. + * For the {@link #getValueReference} method, the (base, property) is + * not resolved by the ELResolver, but an instance of {@link ValueReference} + * is created to encapsulate this (base ,property), and returned. + *

    + * + *

    See the notes about comparison, serialization and immutability in + * the {@link Expression} javadocs. + * + * @see ELResolver + * @see Expression + * @see ExpressionFactory + * @since JSP 2.1 + */ +public abstract class ValueExpression + extends Expression +{ + + /** + * Evaluates the expression relative to the provided context, and + * returns the resulting value. + * + *

    The resulting value is automatically coerced to the type + * returned by getExpectedType(), which was + * provided to the ExpressionFactory when this + * expression was created.

    + * + * @param context The context of this evaluation. + * @return The result of the expression evaluation. + * @throws NullPointerException if context is null. + * @throws PropertyNotFoundException if one of the property + * resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws ELException if an exception was thrown while performing + * property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public abstract Object getValue(ELContext context); + + /** + * Evaluates the expression relative to the provided context, and + * sets the result to the provided value. + * + * @param context The context of this evaluation. + * @param value The new value to be set. + * @throws NullPointerException if context is null. + * @throws PropertyNotFoundException if one of the property + * resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws PropertyNotWritableException if the final variable or + * property resolution failed because the specified + * variable or property is not writable. + * @throws ELException if an exception was thrown while attempting to + * set the property or variable. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public abstract void setValue(ELContext context, Object value); + + /** + * Evaluates the expression relative to the provided context, and + * returns true if a call to {@link #setValue} will + * always fail. + * + * @param context The context of this evaluation. + * @return true if the expression is read-only or + * false if not. + * @throws NullPointerException if context is null. + * @throws PropertyNotFoundException if one of the property + * resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws ELException if an exception was thrown while performing + * property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + * * @throws NullPointerException if context is null + */ + public abstract boolean isReadOnly(ELContext context); + + /** + * Evaluates the expression relative to the provided context, and + * returns the most general type that is acceptable for an object to be + * passed as the value parameter in a future call + * to the {@link #setValue} method. + * + *

    This is not always the same as getValue().getClass(). + * For example, in the case of an expression that references an + * array element, the getType method will return the + * element type of the array, which might be a superclass of the type + * of the actual element that is currently in the specified + * array element.

    + * + * @param context The context of this evaluation. + * @return the most general acceptable type; otherwise undefined. + * @throws NullPointerException if context is null. + * @throws PropertyNotFoundException if one of the property + * resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws ELException if an exception was thrown while performing + * property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if + * available. + */ + public abstract Class getType(ELContext context); + + /** + * Returns the type the result of the expression will be coerced to + * after evaluation. + * + * @return the expectedType passed to the + * ExpressionFactory.createValueExpression method + * that created this ValueExpression. + */ + public abstract Class getExpectedType(); + + /** + * Returns a {@link ValueReference} for this expression instance. + * @param context the context of this evaluation + * @return the ValueReference for this + * ValueExpression, or null if this + * ValueExpression is not a reference to + * a base (null or non-null) and a property. + * If the base is null, and the property is a EL variable, return + * the ValueReference for the + * ValueExpression associated with this EL variable. + * + * @since EL 2.2 + */ + public ValueReference getValueReference(ELContext context) { + return null; + } +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/ValueReference.java b/fine-third-default/fine-javax-el/src/javax/el/ValueReference.java new file mode 100644 index 000000000..722825570 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/ValueReference.java @@ -0,0 +1,70 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.el; + +import java.io.Serializable; + +/** + * This encapsulates a base model object and one of its properties. + * + * @since EL 2.2 + */ + +public class ValueReference implements Serializable { + + private Object base; + private Object property; + + public ValueReference(Object base, Object property) { + + this.base = base; + this.property = property; + } + + public Object getBase() { + return base; + } + + public Object getProperty() { + return property; + } + +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/VariableMapper.java b/fine-third-default/fine-javax-el/src/javax/el/VariableMapper.java new file mode 100644 index 000000000..41ea78661 --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/VariableMapper.java @@ -0,0 +1,93 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + * + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2004 The Apache Software Foundation + * + * 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 javax.el; + +/** + * The interface to a map between EL variables and the EL expressions + * they are associated with. + * + * @since JSP 2.1 + */ + +public abstract class VariableMapper { + + /** + * @param variable The variable name + * @return the ValueExpression assigned to the variable, + * null if there is no previous assignment to this variable. + */ + public abstract ValueExpression resolveVariable( + String variable); + + /** + * Assign a ValueExpression to an EL variable, replacing + * any previously assignment to the same variable. + * The assignment for the variable is removed if + * the expression is null. + * + * @param variable The variable name + * @param expression The ValueExpression to be assigned + * to the variable. + * @return The previous ValueExpression assigned to this variable, + * null if there is no previous assignment to this variable. + */ + public abstract ValueExpression setVariable( + String variable, + ValueExpression expression); +} diff --git a/fine-third-default/fine-javax-el/src/javax/el/package.html b/fine-third-default/fine-javax-el/src/javax/el/package.html new file mode 100644 index 000000000..98d240cea --- /dev/null +++ b/fine-third-default/fine-javax-el/src/javax/el/package.html @@ -0,0 +1,284 @@ + + + + + + + + +Provides the API for the Unified Expression Language 3.0 + +

    The Expression Language (EL) is a simple language originally designed to +satisfy the specific needs of web application developers. It has evloved +into its own specification intended for general use inside and outside of the +web containers.

    + +

    This package contains the classes and interfaces that describe +and define the programmatic access to the Expression Language engine. The API +is logically partitioned as follows: + +

    + +

    EL Context

    + +

    An important goal of the EL is to ensure it can be used in +a variety of environments. It must therefore provide enough flexibility +to adapt to the specific requirements of the environment where it is +being used.

    + +

    Class {@link javax.el.ELContext} is what links +the EL with the specific environment where it is being used. +It provides +the mechanism through which all relevant context for creating or +evaluating an expression is specified. +

    + +

    When EL used in a web container, the creation of ELContext + objects is controlled through the underlying technology. +For example, in JSP, the + JspContext.getELContext() factory method is used. In an +stand-alone environment, a default {@link javax.el.StandardELContext} is +provided.

    + +

    Some technologies provide the ability to add an {@link javax.el.ELContextListener} + so that applications and frameworks can ensure their own context objects + are attached to any newly created ELContext.

    + +

    Expression Objects

    + +

    At the core of the Expression Language is the notion of an expression +that gets parsed according to the grammar defined by the Expression Language.

    + +

    There are two types of expressions defined by the EL: value expressions +and method expressions. A {@link javax.el.ValueExpression} such as +"${customer.name}" can be used either +as an rvalue (return the value associated with property name +of the model object customer) or as an lvalue +(set the value of the property name of the model object +customer).

    + +

    A {@link javax.el.MethodExpression} such as +"${handler.process}" makes it possible to invoke a method +(process) on a specific model object (handler).

    + +

    In version 2.2 and later, either type of EL expression can represent a method +invocation, such as ${trader.buy("JAVA")}, where the arugments to +the method invocation are specified in the expression.

    + +

    All expression classes extend the base class {@link javax.el.Expression}, making them +serializable and forcing them to implement equals() and +hashCode(). Morevover, each method on these expression classes +that actually evaluates an expression receives a parameter +of class {@link javax.el.ELContext}, +which provides the context required to evaluate the expression.

    + +

    Creation of Expressions

    + +

    An expression is created through the {@link javax.el.ExpressionFactory} class. +The factory provides two creation methods; one for each type of expression + supported by the EL.

    + +

    To create an expression, one must provide an {@link javax.el.ELContext}, +a string representing +the expression, and the expected type (ValueExpression) or signature +(MethodExpression). + +The ELContext provides the context necessary to parse an expression. +Specifically, if the expression uses an EL function +(for example ${fn:toUpperCase(customer.name)}) or an +EL variable, then +{@link javax.el.FunctionMapper} and {@link javax.el.VariableMapper} +objects must be available within the ELContext so that EL functions and +EL variables are properly mapped. + +

    Evaluation of Expressions

    +

    The creation and the evaluation of an expression are done in two separate +steps. At the evaluation of an expression, +the {@link javax.el.ELContext} +provides the context necessary to support property and method resolution +for modal objects.

    + +

    A deferred expression is one that is created but not immediately evaluated. +In a JSF request processing life cycle, EL expressions are typically created +in the tree building phase and evaluated in the rendering phrase.

    + +

    Adding parameters to a ValueExpression further enhances the +power of deferred expressions. The {@link javax.el.LambdaExpression} +encapsulates such a construct. A LambdaExpression can be +invoked by supplying the actual parameters at evaluation. It plays +an important role in the support for collections operators.

    + +

    Evaluation Listeners

    +

    By registering {@link javax.el.EvaluationListener}s in ELContext, a user can +receive notifications during the EL expression evaluations. There are three +events that trigger the notification: +

      +
    • Before evaluation
    • +
    • After evaluation
    • +
    • When (base, property) is resolved
    • +

    + +

    Resolution of Model Objects and their Properties

    + +

    Through the {@link javax.el.ELResolver} base class, the EL +features a pluggable mechanism +to resolve model object references as well as properties and method +invocations of these objects.

    + +

    The EL API provides implementations of ELResolver supporting +property resolution for common data types which include +arrays ({@link javax.el.ArrayELResolver}), JavaBeans ({@link javax.el.BeanELResolver}), Lists ({@link javax.el.ListELResolver}), +Maps ({@link javax.el.MapELResolver}), and ResourceBundles ({@link javax.el.ResourceBundleELResolver}).

    + +

    Tools can easily obtain more information about resolvable model objects and their +resolvable properties by calling +method getFeatureDescriptors on the ELResolver. This method exposes objects +of type java.beans.FeatureDescriptor, providing all information of interest +on top-level model objects as well as their properties.

    + +

    EL Functions

    + +

    If an EL expression uses a function +(for example ${fn:toUpperCase(customer.name)}), then a +{@link javax.el.FunctionMapper} +object must also be specified within the ELContext. +The FunctionMapper is responsible to map + ${prefix:name()} style functions to +static methods that can execute the specified functions. +

    + +

    EL Variables

    + +

    Just like {@link javax.el.FunctionMapper} provides +a flexible mechanism to add functions to the EL, {@link javax.el.VariableMapper} +provides a flexible mechanism to support the notion of +EL variables. +

    + +

    +An EL variable does not directly refer to a model object that can then +be resolved by an ELResolver. Instead, it refers to an EL +expression. The evaluation of that EL expression gives the EL variable +its value. +

    + +

    +For example, in the following code snippet +

    + <h:inputText value="#{handler.customer.name}"/> +
    + +handler refers to a model object that can be resolved by an EL Resolver. +

    +

    +However, in this other example: +

    +
    +<c:forEach var="item" items="#{model.list}">
    +   <h:inputText value="#{item.name}"/>
    +</c:forEach>
    +
    +
    + +item is an EL variable because it does not refer directly to a model +object. Instead, it refers to another EL expression, namely a +specific item in the collection referred to by the EL expression +#{model.list}. +

    + +

    +Assuming that there are three elements in ${model.list}, this means +that for +each invocation of <h:inputText>, the following information +about item must be preserved in the {@link javax.el.VariableMapper}: +

    + first invocation: item maps to first element in ${model.list}
    + second invocation: item maps to second element in ${model.list}
    + third invocation: item maps to third element in ${model.list}
    +
    +

    +VariableMapper provides the mechanisms required to allow the mapping +of an EL variable to the EL expression from which it gets its value. +

    + +

    EL in Stand-alone environment

    +

    EL 3.0 includes APIs for using EL in a stand-alone environment.

    +

    {@link javax.el.ELProcessor} provides simple APIs for the direct +evaluations of expressions. It also makes it easy to define functions, +set variables, and define a beans locally.

    + +

    {@link javax.el.ELManager} provides a lower level APIs for managing the EL +parsing and evaluation environment. It contains a default ELContext +{@link javax.el.StandardELContext}.

    + + + + diff --git a/fine-third-default/fine-javax-inject/src/javax/inject/Inject.java b/fine-third-default/fine-javax-inject/src/javax/inject/Inject.java new file mode 100644 index 000000000..43458f8bb --- /dev/null +++ b/fine-third-default/fine-javax-inject/src/javax/inject/Inject.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2009 The JSR-330 Expert Group + * + * 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 javax.inject; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; +import java.lang.annotation.Documented; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; + +/** + * Identifies injectable constructors, methods, and fields. May apply to static + * as well as instance members. An injectable member may have any access + * modifier (private, package-private, protected, public). Constructors are + * injected first, followed by fields, and then methods. Fields and methods + * in superclasses are injected before those in subclasses. Ordering of + * injection among fields and among methods in the same class is not specified. + * + *

    Injectable constructors are annotated with {@code @Inject} and accept + * zero or more dependencies as arguments. {@code @Inject} can apply to at most + * one constructor per class. + * + *

    @Inject + * ConstructorModifiersopt + * SimpleTypeName(FormalParameterListopt) + * Throwsopt + * ConstructorBody
    + * + *

    {@code @Inject} is optional for public, no-argument constructors when no + * other constructors are present. This enables injectors to invoke default + * constructors. + * + *

    + * {@literal @}Injectopt + * Annotationsopt + * public + * SimpleTypeName() + * Throwsopt + * ConstructorBody
    + * + *

    Injectable fields: + *

      + *
    • are annotated with {@code @Inject}. + *
    • are not final. + *
    • may have any otherwise valid name.
    + * + *

    @Inject + * FieldModifiersopt + * Type + * VariableDeclarators;
    + * + *

    Injectable methods: + *

      + *
    • are annotated with {@code @Inject}.
    • + *
    • are not abstract.
    • + *
    • do not declare type parameters of their own.
    • + *
    • may return a result
    • + *
    • may have any otherwise valid name.
    • + *
    • accept zero or more dependencies as arguments.
    + * + *

    @Inject + * MethodModifiersopt + * ResultType + * Identifier(FormalParameterListopt) + * Throwsopt + * MethodBody
    + * + *

    The injector ignores the result of an injected method, but + * non-{@code void} return types are allowed to support use of the method in + * other contexts (builder-style method chaining, for example). + * + *

    Examples: + * + *

    + *   public class Car {
    + *     // Injectable constructor
    + *     @Inject public Car(Engine engine) { ... }
    + *
    + *     // Injectable field
    + *     @Inject private Provider<Seat> seatProvider;
    + *
    + *     // Injectable package-private method
    + *     @Inject void install(Windshield windshield, Trunk trunk) { ... }
    + *   }
    + * + *

    A method annotated with {@code @Inject} that overrides another method + * annotated with {@code @Inject} will only be injected once per injection + * request per instance. A method with no {@code @Inject} annotation + * that overrides a method annotated with {@code @Inject} will not be + * injected. + * + *

    Injection of members annotated with {@code @Inject} is required. While an + * injectable member may use any accessibility modifier (including + * private), platform or injector limitations (like security + * restrictions or lack of reflection support) might preclude injection + * of non-public members. + * + *

    Qualifiers

    + * + *

    A {@linkplain Qualifier qualifier} may annotate an injectable field + * or parameter and, combined with the type, identify the implementation to + * inject. Qualifiers are optional, and when used with {@code @Inject} in + * injector-independent classes, no more than one qualifier should annotate a + * single field or parameter. The qualifiers are bold in the following example: + * + *

    + *   public class Car {
    + *     @Inject private @Leather Provider<Seat> seatProvider;
    + *
    + *     @Inject void install(@Tinted Windshield windshield,
    + *         @Big Trunk trunk) { ... }
    + *   }
    + * + *

    If one injectable method overrides another, the overriding method's + * parameters do not automatically inherit qualifiers from the overridden + * method's parameters. + * + *

    Injectable Values

    + * + *

    For a given type T and optional qualifier, an injector must be able to + * inject a user-specified class that: + * + *

      + *
    1. is assignment compatible with T and
    2. + *
    3. has an injectable constructor.
    4. + *
    + * + *

    For example, the user might use external configuration to pick an + * implementation of T. Beyond that, which values are injected depend upon the + * injector implementation and its configuration. + * + *

    Circular Dependencies

    + * + *

    Detecting and resolving circular dependencies is left as an exercise for + * the injector implementation. Circular dependencies between two constructors + * is an obvious problem, but you can also have a circular dependency between + * injectable fields or methods: + * + *

    + *   class A {
    + *     @Inject B b;
    + *   }
    + *   class B {
    + *     @Inject A a;
    + *   }
    + * + *

    When constructing an instance of {@code A}, a naive injector + * implementation might go into an infinite loop constructing an instance of + * {@code B} to set on {@code A}, a second instance of {@code A} to set on + * {@code B}, a second instance of {@code B} to set on the second instance of + * {@code A}, and so on. + * + *

    A conservative injector might detect the circular dependency at build + * time and generate an error, at which point the programmer could break the + * circular dependency by injecting {@link Provider Provider<A>} or {@code + * Provider} instead of {@code A} or {@code B} respectively. Calling {@link + * Provider#get() get()} on the provider directly from the constructor or + * method it was injected into defeats the provider's ability to break up + * circular dependencies. In the case of method or field injection, scoping + * one of the dependencies (using {@linkplain Singleton singleton scope}, for + * example) may also enable a valid circular relationship. + * + * @see javax.inject.Qualifier @Qualifier + * @see javax.inject.Provider + */ +@Target({ METHOD, CONSTRUCTOR, FIELD }) +@Retention(RUNTIME) +@Documented +public @interface Inject {} diff --git a/fine-third-default/fine-javax-inject/src/javax/inject/Named.java b/fine-third-default/fine-javax-inject/src/javax/inject/Named.java new file mode 100644 index 000000000..2fb4cab9d --- /dev/null +++ b/fine-third-default/fine-javax-inject/src/javax/inject/Named.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 The JSR-330 Expert Group + * + * 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 javax.inject; + +import java.lang.annotation.Retention; +import java.lang.annotation.Documented; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * String-based {@linkplain Qualifier qualifier}. + * + *

    Example usage: + * + *

    + *   public class Car {
    + *     @Inject @Named("driver") Seat driverSeat;
    + *     @Inject @Named("passenger") Seat passengerSeat;
    + *     ...
    + *   }
    + */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface Named { + + /** The name. */ + String value() default ""; +} diff --git a/fine-third-default/fine-javax-inject/src/javax/inject/Provider.java b/fine-third-default/fine-javax-inject/src/javax/inject/Provider.java new file mode 100644 index 000000000..271c373d2 --- /dev/null +++ b/fine-third-default/fine-javax-inject/src/javax/inject/Provider.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 The JSR-330 Expert Group + * + * 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 javax.inject; + +/** + * Provides instances of {@code T}. Typically implemented by an injector. For + * any type {@code T} that can be injected, you can also inject + * {@code Provider}. Compared to injecting {@code T} directly, injecting + * {@code Provider} enables: + * + *
      + *
    • retrieving multiple instances.
    • + *
    • lazy or optional retrieval of an instance.
    • + *
    • breaking circular dependencies.
    • + *
    • abstracting scope so you can look up an instance in a smaller scope + * from an instance in a containing scope.
    • + *
    + * + *

    For example: + * + *

    + *   class Car {
    + *     @Inject Car(Provider<Seat> seatProvider) {
    + *       Seat driver = seatProvider.get();
    + *       Seat passenger = seatProvider.get();
    + *       ...
    + *     }
    + *   }
    + */ +public interface Provider { + + /** + * Provides a fully-constructed and injected instance of {@code T}. + * + * @throws RuntimeException if the injector encounters an error while + * providing an instance. For example, if an injectable member on + * {@code T} throws an exception, the injector may wrap the exception + * and throw it to the caller of {@code get()}. Callers should not try + * to handle such exceptions as the behavior may vary across injector + * implementations and even different configurations of the same injector. + */ + T get(); +} diff --git a/fine-third-default/fine-javax-inject/src/javax/inject/Qualifier.java b/fine-third-default/fine-javax-inject/src/javax/inject/Qualifier.java new file mode 100644 index 000000000..6b4c42d5d --- /dev/null +++ b/fine-third-default/fine-javax-inject/src/javax/inject/Qualifier.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 The JSR-330 Expert Group + * + * 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 javax.inject; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; +import java.lang.annotation.Documented; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; + +/** + * Identifies qualifier annotations. Anyone can define a new qualifier. A + * qualifier annotation: + * + *
      + *
    • is annotated with {@code @Qualifier}, {@code @Retention(RUNTIME)}, + * and typically {@code @Documented}.
    • + *
    • can have attributes.
    • + *
    • may be part of the public API, much like the dependency type, but + * unlike implementation types which needn't be part of the public + * API.
    • + *
    • may have restricted usage if annotated with {@code @Target}. While + * this specification covers applying qualifiers to fields and + * parameters only, some injector configurations might use qualifier + * annotations in other places (on methods or classes for example).
    • + *
    + * + *

    For example: + * + *

    + *   @java.lang.annotation.Documented
    + *   @java.lang.annotation.Retention(RUNTIME)
    + *   @javax.inject.Qualifier
    + *   public @interface Leather {
    + *     Color color() default Color.TAN;
    + *     public enum Color { RED, BLACK, TAN }
    + *   }
    + * + * @see javax.inject.Named @Named + */ +@Target(ANNOTATION_TYPE) +@Retention(RUNTIME) +@Documented +public @interface Qualifier {} diff --git a/fine-third-default/fine-javax-inject/src/javax/inject/Scope.java b/fine-third-default/fine-javax-inject/src/javax/inject/Scope.java new file mode 100644 index 000000000..27133f1ad --- /dev/null +++ b/fine-third-default/fine-javax-inject/src/javax/inject/Scope.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2009 The JSR-330 Expert Group + * + * 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 javax.inject; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; +import java.lang.annotation.Documented; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; + +/** + * Identifies scope annotations. A scope annotation applies to a class + * containing an injectable constructor and governs how the injector reuses + * instances of the type. By default, if no scope annotation is present, the + * injector creates an instance (by injecting the type's constructor), uses + * the instance for one injection, and then forgets it. If a scope annotation + * is present, the injector may retain the instance for possible reuse in a + * later injection. If multiple threads can access a scoped instance, its + * implementation should be thread safe. The implementation of the scope + * itself is left up to the injector. + * + *

    In the following example, the scope annotation {@code @Singleton} ensures + * that we only have one Log instance: + * + *

    + *   @Singleton
    + *   class Log {
    + *     void log(String message) { ... }
    + *   }
    + * + *

    The injector generates an error if it encounters more than one scope + * annotation on the same class or a scope annotation it doesn't support. + * + *

    A scope annotation: + *

      + *
    • is annotated with {@code @Scope}, {@code @Retention(RUNTIME)}, + * and typically {@code @Documented}.
    • + *
    • should not have attributes.
    • + *
    • is typically not {@code @Inherited}, so scoping is orthogonal to + * implementation inheritance.
    • + *
    • may have restricted usage if annotated with {@code @Target}. While + * this specification covers applying scopes to classes only, some + * injector configurations might use scope annotations + * in other places (on factory method results for example).
    • + *
    + * + *

    For example: + * + *

    + *   @java.lang.annotation.Documented
    + *   @java.lang.annotation.Retention(RUNTIME)
    + *   @javax.inject.Scope
    + *   public @interface RequestScoped {}
    + * + *

    Annotating scope annotations with {@code @Scope} helps the injector + * detect the case where a programmer used the scope annotation on a class but + * forgot to configure the scope in the injector. A conservative injector + * would generate an error rather than not apply a scope. + * + * @see javax.inject.Singleton @Singleton + */ +@Target(ANNOTATION_TYPE) +@Retention(RUNTIME) +@Documented +public @interface Scope {} diff --git a/fine-third-default/fine-javax-inject/src/javax/inject/Singleton.java b/fine-third-default/fine-javax-inject/src/javax/inject/Singleton.java new file mode 100644 index 000000000..a2af7b956 --- /dev/null +++ b/fine-third-default/fine-javax-inject/src/javax/inject/Singleton.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009 The JSR-330 Expert Group + * + * 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 javax.inject; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Identifies a type that the injector only instantiates once. Not inherited. + * + * @see javax.inject.Scope @Scope + */ +@Scope +@Documented +@Retention(RUNTIME) +public @interface Singleton {} diff --git a/fine-third-default/fine-javax-inject/src/javax/inject/package-info.java b/fine-third-default/fine-javax-inject/src/javax/inject/package-info.java new file mode 100644 index 000000000..52474109e --- /dev/null +++ b/fine-third-default/fine-javax-inject/src/javax/inject/package-info.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2009 The JSR-330 Expert Group + * + * 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. + */ + +/** + * This package specifies a means for obtaining objects in such a way as to + * maximize reusability, testability and maintainability compared to + * traditional approaches such as constructors, factories, and service + * locators (e.g., JNDI). This process, known as dependency + * injection, is beneficial to most nontrivial applications. + * + *

    Many types depend on other types. For example, a Stopwatch might + * depend on a TimeSource. The types on which a type depends are + * known as its dependencies. The process of finding an instance of a + * dependency to use at run time is known as resolving the dependency. + * If no such instance can be found, the dependency is said to be + * unsatisfied, and the application is broken. + * + *

    In the absence of dependency injection, an object can resolve its + * dependencies in a few ways. It can invoke a constructor, hard-wiring an + * object directly to its dependency's implementation and life cycle: + * + *

       class Stopwatch {
    + *     final TimeSource timeSource;
    + *     Stopwatch () {
    + *       timeSource = new AtomicClock(...);
    + *     }
    + *     void start() { ... }
    + *     long stop() { ... }
    + *   }
    + * + *

    If more flexibility is needed, the object can call out to a factory or + * service locator: + * + *

       class Stopwatch {
    + *     final TimeSource timeSource;
    + *     Stopwatch () {
    + *       timeSource = DefaultTimeSource.getInstance();
    + *     }
    + *     void start() { ... }
    + *     long stop() { ... }
    + *   }
    + * + *

    In deciding between these traditional approaches to dependency + * resolution, a programmer must make trade-offs. Constructors are more + * concise but restrictive. Factories decouple the client and implementation + * to some extent but require boilerplate code. Service locators decouple even + * further but reduce compile time type safety. All three approaches inhibit + * unit testing. For example, if the programmer uses a factory, each test + * against code that depends on the factory will have to mock out the factory + * and remember to clean up after itself or else risk side effects: + * + *

       void testStopwatch() {
    + *     TimeSource original = DefaultTimeSource.getInstance();
    + *     DefaultTimeSource.setInstance(new MockTimeSource());
    + *     try {
    + *       // Now, we can actually test Stopwatch.
    + *       Stopwatch sw = new Stopwatch();
    + *       ...
    + *     } finally {
    + *       DefaultTimeSource.setInstance(original);
    + *     }
    + *   }
    + * + *

    In practice, supporting this ability to mock out a factory results in + * even more boilerplate code. Tests that mock out and clean up after multiple + * dependencies quickly get out of hand. To make matters worse, a programmer + * must predict accurately how much flexibility will be needed in the future + * or else suffer the consequences. If a programmer initially elects to use a + * constructor but later decides that more flexibility is required, the + * programmer must replace every call to the constructor. If the programmer + * errs on the side of caution and write factories up front, it may result in + * a lot of unnecessary boilerplate code, adding noise, complexity, and + * error-proneness. + * + *

    Dependency injection addresses all of these issues. Instead of + * the programmer calling a constructor or factory, a tool called a + * dependency injector passes dependencies to objects: + * + *

       class Stopwatch {
    + *     final TimeSource timeSource;
    + *     @Inject Stopwatch(TimeSource timeSource) {
    + *       this.timeSource = timeSource;
    + *     }
    + *     void start() { ... }
    + *     long stop() { ... }
    + *   }
    + * + *

    The injector further passes dependencies to other dependencies until it + * constructs the entire object graph. For example, suppose the programmer + * asked an injector to create a StopwatchWidget instance: + * + *

       /** GUI for a Stopwatch */
    + *   class StopwatchWidget {
    + *     @Inject StopwatchWidget(Stopwatch sw) { ... }
    + *     ...
    + *   }
    + * + *

    The injector might: + *

      + *
    1. Find a TimeSource + *
    2. Construct a Stopwatch with the TimeSource + *
    3. Construct a StopwatchWidget with the Stopwatch + *
    + * + *

    This leaves the programmer's code clean, flexible, and relatively free + * of dependency-related infrastructure. + * + *

    In unit tests, the programmer can now construct objects directly + * (without an injector) and pass in mock dependencies. The programmer no + * longer needs to set up and tear down factories or service locators in each + * test. This greatly simplifies our unit test: + * + *

       void testStopwatch() {
    + *     Stopwatch sw = new Stopwatch(new MockTimeSource());
    + *     ...
    + *   }
    + * + *

    The total decrease in unit-test complexity is proportional to the + * product of the number of unit tests and the number of dependencies. + * + *

    This package provides dependency injection annotations that enable + * portable classes, but it leaves external dependency configuration up to + * the injector implementation. Programmers annotate constructors, methods, + * and fields to advertise their injectability (constructor injection is + * demonstrated in the examples above). A dependency injector identifies a + * class's dependencies by inspecting these annotations, and injects the + * dependencies at run time. Moreover, the injector can verify that all + * dependencies have been satisfied at build time. A service locator, + * by contrast, cannot detect unsatisfied dependencies until run time. + * + *

    Injector implementations can take many forms. An injector could + * configure itself using XML, annotations, a DSL (domain-specific language), + * or even plain Java code. An injector could rely on reflection or code + * generation. An injector that uses compile-time code generation may not even + * have its own run time representation. Other injectors may not be able to + * generate code at all, neither at compile nor run time. A "container", for + * some definition, can be an injector, but this package specification aims to + * minimize restrictions on injector implementations. + * + * @see javax.inject.Inject @Inject + */ +package javax.inject; diff --git a/fine-third-default/fine-javax-interceptor/src/javax/interceptor/AroundConstruct.java b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/AroundConstruct.java new file mode 100644 index 000000000..78d167e46 --- /dev/null +++ b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/AroundConstruct.java @@ -0,0 +1,87 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.interceptor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + *

    Designates an interceptor method that receives a callback when + * the target class constructor is invoked. + *

    + *

    The method to which the AroundConstruct annotation is applied must have one of the + * following signatures. + *

    + * + *
    + * void <METHOD>(InvocationContext ctx) 
    + *
    + * Object <METHOD>(InvocationContext ctx) 
    + * 
    + * + *

    The method must not be declared as abstract, final, or static.

    + * + *

    An AroundConstruct interceptor method may be only declared in + * an interceptor class or superclass of an interceptor class.

    + * + *

    An interceptor class must not declare more than one AroundConstruct + * method.

    + * + *

    The target instance is created and its constructor injection is + * performed, if applicable, when the last interceptor method in the + * AroundConstruct interceptor chain invokes the + * {@link javax.interceptor.InvocationContext#proceed()} method. + * + *

    An AroundConstruct interceptor method should exercise caution + * accessing the instance whose constructor it interposes on.

    + * + *

    AroundConstruct methods may throw any exceptions that are + * allowed by the throws clause of the constructor on which they are + * interposing.

    + * + * @since Interceptors 1.2 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AroundConstruct { +} diff --git a/fine-third-default/fine-javax-interceptor/src/javax/interceptor/AroundInvoke.java b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/AroundInvoke.java new file mode 100644 index 000000000..b8f9ae46e --- /dev/null +++ b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/AroundInvoke.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.interceptor; + +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +/** + *

    Defines an interceptor method that interposes on business methods. + * The method must take a single parameter of type + * {@link javax.interceptor.InvocationContext} and have a return type + * {@link java.lang.Object}. The method must not be declared as abstract, + * final, or static.

    + * + *
    + * @AroundInvoke
    + * public Object intercept(InvocationContext ctx) throws Exception { ... }
    + * 
    + * + *

    AroundInvoke methods may be declared in interceptor + * classes, in the superclasses of interceptor classes, in the target + * class, and/or in superclasses of the target class.

    + * + *

    A given class must not declare more than one AroundInvoke + * method.

    + * + *

    An AroundInvoke method can invoke any component or + * resource that the method it is intercepting can invoke.

    + * + *

    In general, AroundInvoke method invocations occur within the + * same transaction and security context as the method on which they are + * interposing.

    + * + *

    AroundInvoke methods may throw any exceptions that are + * allowed by the throws clause of the method on which they are + * interposing. They may catch and suppress exceptions and recover + * by calling {@link javax.interceptor.InvocationContext#proceed()}.

    + * + * @since Interceptors 1.0 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AroundInvoke { +} diff --git a/fine-third-default/fine-javax-interceptor/src/javax/interceptor/AroundTimeout.java b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/AroundTimeout.java new file mode 100644 index 000000000..78a60c227 --- /dev/null +++ b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/AroundTimeout.java @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.interceptor; + +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +/** + *

    Defines an interceptor method that interposes on timeout methods. + * The method must take a single parameter of type + * {@link javax.interceptor.InvocationContext} and have a return type + * {@link java.lang.Object}. The method must not be declared as abstract, + * final, or static.

    + * + *
    + * @AroundTimeout
    + * public Object interceptTimeout(InvocationContext ctx) throws Exception { ... }
    + * 
    + * + *

    AroundTimeout methods may be declared in interceptor + * classes, in the superclasses of interceptor classes, in the target + * class, and/or in superclasses of the target class.

    + * + *

    A given class must not declare more than one AroundTimeout + * method.

    + * + *

    An AroundTimeout method can invoke any component or + * resource that its corresponding timeout method can invoke.

    + * + *

    {@link javax.interceptor.InvocationContext#getTimer()} allows any + * AroundTimeout method to retrieve the timer object + * associated with the timeout.

    + * + *

    In general, AroundTimeout method invocations occur within the + * same transaction and security context as the timeout method on which they + * are interposing.

    + * + *

    AroundTimeout methods may throw any exceptions that are + * allowed by the throws clause of the timeout method on which they are + * interposing. They may catch and suppress exceptions and recover + * by calling {@link javax.interceptor.InvocationContext#proceed()}.

    + * + * @since Interceptors 1.1 + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AroundTimeout { +} diff --git a/fine-third-default/fine-javax-interceptor/src/javax/interceptor/ExcludeClassInterceptors.java b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/ExcludeClassInterceptors.java new file mode 100644 index 000000000..c85c0a970 --- /dev/null +++ b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/ExcludeClassInterceptors.java @@ -0,0 +1,70 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.interceptor; + +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +/** + *

    Used to exclude class-level interceptors for the + * lifecycle callback method, business method, timeout method, + * or constructor to which it is applied.

    + * + *

    Excludes interceptors defined by means of the + * {@link javax.interceptor.Interceptors} annotation. + * Use of this annotation to exclude interceptors defined by means + * of interceptor binding annotations is not portable.

    + * + *
    + * @ExcludeClassInterceptors
    + * public void updateOrder(Order order) { ... }
    + * 
    + * + * @see javax.interceptor.ExcludeDefaultInterceptors + * + * @since Interceptors 1.0 + */ +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcludeClassInterceptors {} diff --git a/fine-third-default/fine-javax-interceptor/src/javax/interceptor/ExcludeDefaultInterceptors.java b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/ExcludeDefaultInterceptors.java new file mode 100644 index 000000000..16aa36ecd --- /dev/null +++ b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/ExcludeDefaultInterceptors.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.interceptor; + +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +/** + *

    Used to exclude default interceptors for a target class or for + * a lifecycle callback method, business method, timeout method, + * or constructor of a target class.

    + * + *
    + * @ExcludeDefaultInterceptors
    + * @Interceptors(ValidationInterceptor.class)
    + * public class Order { ... }
    + * 
    + * + *
    + * @ExcludeDefaultInterceptors
    + * public void updateOrder(Order order) { ... }
    + * 
    + * + * @since Interceptors 1.0 + */ +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcludeDefaultInterceptors {} diff --git a/fine-third-default/fine-javax-interceptor/src/javax/interceptor/Interceptor.java b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/Interceptor.java new file mode 100644 index 000000000..4bf3b7f05 --- /dev/null +++ b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/Interceptor.java @@ -0,0 +1,169 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.interceptor; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + *

    Specifies that a class is an interceptor.

    + * + *
    + * @Validated @Interceptor
    + * public class ValidationInterceptor { ... }
    + * 
    + * + *

    Use of this annotation is required when declaring an interceptor + * using interceptor binding annotations. + * It is ignored during the processing of classes bound using the {@link + * javax.interceptor.Interceptors Interceptors} annotation (or when + * the EJB deployment descriptor is used to associate the interceptor + * with the target class).

    + * + * @see javax.interceptor.Interceptors + * + * @since Interceptors 1.1 + */ +@Retention(RUNTIME) +@Target(TYPE) +@Documented +public @interface Interceptor { + /** + *

    Priorities that define the order in which interceptors are + * invoked. These values are intended to be used with the + * {@link javax.annotation.Priority Priority} annotation for + * interceptors that are defined by means of interceptor binding. + * + *

    Interceptors with smaller priority values are called first. + * If more than one interceptor has the same priority, the relative + * order of those interceptors is undefined.

    + * + *
      + *
    • Interceptors defined by platform specifications that + * are to be executed at the beginning of the interceptor chain + * should have priority values in the range PLATFORM_BEFORE up until LIBRARY_BEFORE.
    • + * + *
    • Interceptors defined by extension libraries that are + * intended to be executed earlier in the interceptor chain, but + * after any interceptors in the range up until LIBRARY_BEFORE should have priority + * values in the range LIBRARY_BEFORE up until APPLICATION.
    • + * + *
    • Interceptors defined by applications should have priority values + * in the range APPLICATION up until + * LIBRARY_AFTER.
    • + * + *
    • Interceptors defined by extension libraries that are + * intended to be executed later in the interceptor chain + * should have priority values in the range LIBRARY_AFTER up until PLATFORM_AFTER.
    • + * + *
    • Interceptors defined by platform specifications that are + * intended to be executed at the end of the interceptor chain + * should have priority values at PLATFORM_AFTER or higher.
    • + *
    + * + *

    An interceptor that must be invoked before or + * after another defined interceptor can choose any appropriate + * value.

    + * + * + *

    For example, an extension library might define an interceptor + * like this:

    + * + *
    +     * @Priority(Interceptor.Priority.LIBRARY_BEFORE+10)
    +     * @Validated @Interceptor
    +     * public class ValidationInterceptor { ... }
    +     * 
    + * + * The {@link javax.annotation.Priority Priority} annotation is + * ignored when computing the invocation order of interceptors + * bound to a target using the {@link javax.interceptor.Interceptors + * Interceptors} annotation. + * + * @see javax.annotation.Priority + * @since Interceptors 1.2 + */ + public static class Priority { + private Priority() { } // don't allow instances + + /** + * Start of range for early interceptors defined by + * platform specifications. + */ + public static final int PLATFORM_BEFORE = 0; + + /** + * Start of range for early interceptors defined by + * extension libraries. + */ + public static final int LIBRARY_BEFORE = 1000; + + /** + * Start of range for interceptors defined by + * applications. + */ + public static final int APPLICATION = 2000; + + /** + * Start of range for late interceptors defined by + * extension libraries. + */ + public static final int LIBRARY_AFTER = 3000; + + /** + * Start of range for late interceptors defined by + * platform specifications. + */ + public static final int PLATFORM_AFTER = 4000; + } +} diff --git a/fine-third-default/fine-javax-interceptor/src/javax/interceptor/InterceptorBinding.java b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/InterceptorBinding.java new file mode 100644 index 000000000..51681b7f9 --- /dev/null +++ b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/InterceptorBinding.java @@ -0,0 +1,120 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.interceptor; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + *

    Specifies that an annotation type is an interceptor binding type.

    + * + *
    + * @Inherited 
    + * @InterceptorBinding 
    + * @Target({TYPE, METHOD, CONSTRUCTOR}) 
    + * @Retention(RUNTIME) 
    + * public @interface Valid {}
    + * 
    + * + *

    Interceptor bindings + * are intermediate annotations that may be used to associate + * interceptors with target beans.

    + * + *

    The interceptor bindings of an interceptor are specified by annotating + * the interceptor class with the binding types and the + * {@link javax.interceptor.Interceptor Interceptor} annotation.

    + * + *
    + * @Valid @Interceptor
    + * public class ValidationInterceptor { ... }
    + * 
    + * + *

    An interceptor may specify multiple interceptor bindings.

    + * + *

    An interceptor binding of a bean + * may be declared by annotating the bean class, a method of the bean class, + * or a constructor of the bean class with the interceptor binding type.

    + * + *
    + * @Valid
    + * public class Order { ... }
    + * 
    + * + *
    + * @Valid @Secure
    + * public void updateOrder(Order order) { ... }
    + * 
    + * + *
    + * @Valid
    + * public Order(...) { ... }
    + * 
    + * + *

    A bean class or method of a bean class may declare multiple interceptor + * bindings.

    + * + *

    An interceptor binding type may declare other interceptor bindings.

    + * + *
    + * @Inherited 
    + * @InterceptorBinding 
    + * @Target({TYPE, METHOD}) 
    + * @Retention(RUNTIME) 
    + * @Valid
    + * public @interface Secure {}
    + * 
    + * + *

    Interceptor bindings are transitive—an interceptor binding declared + * by an interceptor binding type is inherited by all beans and other interceptor + * binding types that declare that interceptor binding type.

    + * + * @see javax.interceptor.Interceptor + * + * @since Interceptors 1.1 + */ +@Target(ANNOTATION_TYPE) +@Retention(RUNTIME) +@Documented +public @interface InterceptorBinding {} diff --git a/fine-third-default/fine-javax-interceptor/src/javax/interceptor/Interceptors.java b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/Interceptors.java new file mode 100644 index 000000000..cf588862a --- /dev/null +++ b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/Interceptors.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.interceptor; + +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.ElementType; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +/** + *

    Declares an ordered list of interceptors for a target class, or + * for a method or a constructor of a target class.

    + * + *
    + * @Interceptors(ValidationInterceptor.class)
    + * public class Order { ... }
    + * 
    + * + *
    + * @Interceptors({ValidationInterceptor.class, SecurityInterceptor.class})
    + * public void updateOrder(Order order) { ... }
    + * 
    + * + *

    Only business method interception or timeout method interception may be specified + * by a method-level Interceptors declaration.

    + * + *

    Constructor interception may be specified + * by a constructor-level Interceptors declaration.

    + * + * @see javax.interceptor.ExcludeClassInterceptors + * @see javax.interceptor.ExcludeDefaultInterceptors + * + * @since Interceptors 1.0 + */ +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Interceptors { + + /** + * An ordered list of interceptors. + */ + Class[] value(); +} diff --git a/fine-third-default/fine-javax-interceptor/src/javax/interceptor/InvocationContext.java b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/InvocationContext.java new file mode 100644 index 000000000..ac945bd70 --- /dev/null +++ b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/InvocationContext.java @@ -0,0 +1,189 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.interceptor; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Map; + +/** + *

    Exposes contextual information about the intercepted invocation and + * operations that enable interceptor methods to control the behavior + * of the invocation chain.

    + * + *
    + *
    + *    @AroundInvoke
    + *    public Object logInvocation(InvocationContext ctx) throws Exception {
    + *       String class = ctx.getMethod().getDeclaringClass().getName();
    + *       String method = ctx.getMethod().getName();
    + *       Logger.global.entering(class, method, ctx.getParameters());
    + *       try {
    + *          Object result = ctx.proceed();
    + *          Logger.global.exiting(class, method, result);
    + *          return result;
    + *       }
    + *       catch (Exception e) {
    + *          Logger.global.throwing(class, method, e);
    + *          throw e;
    + *       }
    + *
    + *    }
    + * 
    + * 
    + * + * @since Interceptors 1.0 + */ +public interface InvocationContext { + + /** + * Returns the target instance. For {@link AroundConstruct} lifecycle callback + * interceptor methods, the getTarget method returns null + * if called before the {@link #proceed} method. + * + * @return the target instance + */ + public Object getTarget(); + + /** + * Returns the timer object associated with a timeout + * method invocation on the target class, or a null value for + * interceptor method types other than {@link AroundTimeout}. + * For example, when associated with an EJB component timeout, this + * method returns {@link javax.ejb.Timer}. + * + * @return the timer object or a null value + * + * @since Interceptors 1.1 + */ + public Object getTimer(); + + /** + * Returns the method of the target class for which the interceptor + * was invoked. Returns null in a lifecycle callback interceptor + * for which there is no corresponding lifecycle callback method + * declared in the target class (or inherited from a superclass) + * or in an {@link AroundConstruct} lifecycle callback interceptor method. + * + * @return the method, or a null value + */ + public Method getMethod(); + + /** + * Returns the constructor of the target class for which the + * {@link AroundConstruct} interceptor method was invoked. + * Returns null for interceptor method types other than + * {@link AroundConstruct} interceptor methods. + * + * @return the constructor, or a null value + */ + public Constructor getConstructor(); + + /** + * Returns the parameter values that will be passed to the method or + * constructor of the target class. If {@link #setParameters} has been + * called, getParameters returns the values to which the parameters + * have been set. + * + * @return the parameter values, as an array + * + * @exception java.lang.IllegalStateException if invoked within + * a lifecycle callback method that is not an {@link AroundConstruct} callback. + */ + public Object[] getParameters(); + + /** + * Sets the parameter values that will be passed to the method or + * constructor of the target class. + * + * @exception java.lang.IllegalStateException if invoked within + * a lifecycle callback method that is not an {@link AroundConstruct} callback. + * + * @exception java.lang.IllegalArgumentException if the types of the + * given parameter values do not match the types of the method or constructor + * parameters, or if the number of parameters supplied does not equal the + * number of method or constructor parameters (if the last parameter is a + * vararg parameter of type T, it is considered to be equivalent + * to a parameter of type T[]). + * + * @param params the parameter values, as an array + */ + public void setParameters(Object[] params); + + /** + * Enables an interceptor to retrieve or update the data associated with + * the invocation by another interceptor, business method, and/or + * webservices endpoint in the invocation chain. If interceptors + * are invoked as a result of the invocation on a web service endpoint, + * the returned value will be an instance of javax.xml.rpc.handler.MessageContext. + * + * @return the context data associated with this invocation or + * lifecycle callback. If there is no context data, an + * empty {@code Map} object will be returned. + * + * @return the context data, as a map + */ + public Map getContextData(); + + /** + * Proceed to the next interceptor in the interceptor chain. For + * around-invoke or around-timeout interceptor methods, the invocation of + * {@code proceed} in the last interceptor method in the chain causes + * the invocation of the target class method. For {@link AroundConstruct} + * lifecycle callback interceptor methods, the invocation of + * {@code proceed} in the last interceptor method in the chain causes + * the target instance to be created. For all other lifecycle callback + * interceptor methods, if there is no callback method defined on the + * target class, the invocation of proceed in the last interceptor method + * in the chain is a no-op. + * + *

    Return the result of the next method invoked, or a null + * value if the method has return type void. + * + *

    + * + * @return the return value of the next method in the chain + * + * @exception Exception if thrown by target method or interceptor method in call stack + */ + public Object proceed() throws Exception; + +} diff --git a/fine-third-default/fine-javax-interceptor/src/javax/interceptor/package.html b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/package.html new file mode 100644 index 000000000..2a111ea03 --- /dev/null +++ b/fine-third-default/fine-javax-interceptor/src/javax/interceptor/package.html @@ -0,0 +1,256 @@ + + + + + + + + +

    Contains annotations and interfaces for defining interceptor methods and interceptor +classes, and for binding interceptor classes to target classes.

    + +

    Interceptor methods

    + +

    An interceptor method is a method of an interceptor class or of a target class +that is invoked to interpose on the invocation of a method of the target class, +a constructor of the target class, a lifecycle event of the target class, or a +timeout method of the target class.

    + +

    +An interceptor method for a target class may be declared in the target class, in +an interceptor class associated with the target class, or in a superclass of +the target class or interceptor class.

    + +

    An {@link javax.interceptor.AroundConstruct} interceptor method may +be defined only in an interceptor class or superclass of an interceptor class.

    + +

    The Interceptors specification defines the interceptor method types listed +below. Extension specifications may define additional interceptor method types.

    + +
      +
    • {@link javax.interceptor.AroundInvoke} interceptor methods, +which interpose on business methods of the target class.
    • + +
    • {@link javax.interceptor.AroundTimeout} interceptor methods, +which interpose on the invocation of timeout methods, in response to timer events.
    • + +
    • {@link javax.annotation.PostConstruct} interceptor methods, which are invoked +after dependency injection has been completed on the target instance.
    • + +
    • {@link javax.annotation.PreDestroy} interceptor methods, which are invoked +before the target instance and all interceptor instances associated with it are +destroyed.
    • + +
    • {@link javax.interceptor.AroundConstruct} interceptor methods, which interpose +on the invocation of the constructor of the target instance.
    • +
    + +{@link javax.annotation.PostConstruct}, {@link +javax.annotation.PreDestroy}, and {@link +javax.interceptor.AroundConstruct} interceptor methods are +collectively referred to as lifecycle callback interceptor methods. + +

    An interceptor method may be defined using annotations or, +optionally, by means of a deployment descriptor. Interceptor methods +may not be declared abstract, static, or +final.

    + +

    An interceptor class or target class may have multiple interceptor methods. However, +an interceptor class or target class may have no more than one interceptor method of a +given interceptor method type: {@link javax.interceptor.AroundInvoke}, +{@link javax.interceptor.AroundTimeout}, {@link javax.annotation.PostConstruct}, +{@link javax.annotation.PreDestroy}, {@link javax.interceptor.AroundConstruct}.

    + +

    Interceptor classes

    + +

    An interceptor class is a class (distinct from the target class) whose methods are +invoked in response to invocations and/or lifecycle events on the target class. Any +number of interceptor classes may be associated with a target class.

    + +

    An interceptor class must have a public constructor with no parameters.

    + +

    Interceptor methods and interceptor classes may be defined for a class by means +of metadata annotations or, optionally, by means of a deployment descriptor.

    + +

    Associating an interceptor class with the target class

    + +

    An interceptor class may be associated with the target class or a method of the +target class in several ways:

    + +
      +
    • By annotating both the interceptor class and the target class with an + interceptor binding annotation. The set of + interceptor bindings for the interceptor are specified by annotating the interceptor + class with the binding types and the {@link javax.interceptor.Interceptor} + annotation.
    • +
    • By using the {@link javax.interceptor.Interceptors Interceptors} annotation + to specify and associate one or more interceptor classes + with a target class or method or constructor of a target class.
    • +
    • If a deployment descriptor is supported, it can be used to associate interceptor + classes with the target class and/or methods of the target class and specify the + order of interceptor invocation or override metadata specified by annotations.
    • +
    + +

    Any interceptor class may be defined to apply to a target class at the class level. +In the case of around-invoke method interceptors, the interceptor applies to all +business methods of the target class. In the case of timeout method interceptors, +the interceptor applies to all timeout methods of the target class.

    + +

    The {@link javax.interceptor.ExcludeClassInterceptors} annotation or, if supported, +a deployment descriptor may be used to exclude the invocation of class level +interceptors defined by the {@link javax.interceptor.Interceptors Interceptors} annotation +for a method or constructor of a target class.

    + +

    An around-invoke interceptor may be defined to apply only to a specific method of the +target class. Likewise, an around-timeout interceptor may be defined to apply only to +a specific timeout method of the target class. However, if an interceptor class that +defines lifecycle callback interceptor methods is defined to apply to a target class +at the method level, the lifecycle callback interceptor methods are not invoked.

    + +

    Default Interceptors

    + +

    Default interceptors are interceptors that apply to a set of target classes. If a +deployment descriptor is supported, it may be used to define default interceptors and +their relative ordering.

    + +

    The {@link javax.interceptor.ExcludeDefaultInterceptors} annotation may be used to +exclude the invocation of default interceptors for a target class or method or constructor of a target class.

    + +

    Interceptor lifecycle

    + +

    The lifecycle of an interceptor instance is the same as that of the target class +instance with which it is associated. Except as noted below for {@link javax.interceptor.AroundConstruct} +lifecycle callback interceptors, when the target instance is created, a +corresponding interceptor instance is created for each associated interceptor class. +These interceptor instances are destroyed when the target instance fails to be created or +when it is removed.

    + +

    An interceptor class shares the enterprise naming context of its associated target +class. Annotations and/or XML deployment descriptor elements for dependency injection or +for direct JNDI lookup refer to this shared naming context.

    + +

    An interceptor instance may hold state. An interceptor instance may be the target +of dependency injection. Dependency injection is performed when the interceptor instance +is created, using the naming context of the associated target class.

    + +

    With the exception of +of {@link javax.interceptor.AroundConstruct} lifecycle callback interceptors, +no interceptor methods are invoked until after dependency injection has been +completed on both the interceptor instances and the target instance.

    + +

    {@link javax.annotation.PostConstruct} interceptor methods, if any, are invoked after +dependency injection has taken place on both the interceptor instances and the target +instance.

    + +

    {@link javax.annotation.PreDestroy} interceptor methods, if any, are +invoked before the target instance and all interceptor instances associated with it are +destroyed.

    + +

    When a {@link javax.interceptor.AroundConstruct} lifecycle callback interceptor +is used, the following rules apply: +

      +
    • The {@link javax.interceptor.AroundConstruct} lifecycle callback is invoked + after dependency injection has been completed on instances of all interceptor + classes associated with the target class. Injection of the target component into + interceptor instances that are invoked during the {@link javax.interceptor.AroundConstruct} + lifecycle callback is not supported.
    • +
    • The target instance is created and its constructor injection is performed, if applicable, + after the last interceptor method in the {@link javax.interceptor.AroundConstruct} + interceptor chain invokes the {@link javax.interceptor.InvocationContext#proceed InvocationContext.proceed()} method. + If the {@link javax.interceptor.InvocationContext#proceed InvocationContext.proceed()} method is not invoked by an + interceptor method, the target instance will not be created.
    • +
    • The {@link javax.interceptor.AroundConstruct} interceptor method can access + the constructed instance using the {@link javax.interceptor.InvocationContext#getTarget InvocationContext.getTarget()} + method after the {@link javax.interceptor.InvocationContext#proceed InvocationContext.proceed()} completes.
    • +
    • Dependency injection on the target instance is not completed until after invocations + of all interceptor methods in the {@link javax.interceptor.AroundConstruct} interceptor chain complete successfully. +
    • The {@link javax.annotation.PostConstruct} lifecycle callback chain for the + target instance, if any, will be invoked after dependency injection has been completed + on the target instance.
    • +
    • An {@link javax.interceptor.AroundConstruct} lifecycle callback interceptor + method should exercise caution when invoking methods of the target instance since its + dependency injection may not have been completed.
    • +
    +

    + +

    Interceptors for lifecycle callbacks

    + +

    A lifecycle callback interceptor method is a non-final, non-static method. A +lifecycle callback interceptor method declared by the target class (or superclass) must +have no parameters. A lifecycle callback interceptor method declared by an interceptor +class must have a single parameter of type {@link javax.interceptor.InvocationContext}.

    + +
    +@PostConstruct
    +public void interceptPostConstruct(InvocationContext ctx) { ... }
    +
    + +

    A single lifecycle callback interceptor method may be used to interpose on multiple +callback events.

    + +
    +@PostConstruct @PreDestroy
    +public void interceptLifecycle(InvocationContext ctx) { ... }
    +
    + +

    A class may not declare more than one lifecycle callback interceptor method for +a particular lifecycle event.

    + +

    Lifecycle callback interceptor methods are invoked in an unspecified security context. +Lifecycle callback interceptor methods are invoked in a transaction context determined +by their target class and/or method. The transaction context may be also changed by +transactional interceptors in the invocation chain.

    + +

    Lifecycle callback interceptor methods may throw runtime exceptions but not checked +exceptions, except for {@link javax.interceptor.AroundConstruct} methods, which may +throw may throw any exceptions that are allowed by the throws clause of the constructor +on which they are interposing.

    + +@see javax.interceptor.AroundConstruct +@see javax.interceptor.AroundInvoke +@see javax.interceptor.AroundTimeout +@see javax.interceptor.Interceptors +@see javax.interceptor.InvocationContext + + + diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/BytesMessage.java b/fine-third-default/fine-javax-jms/src/javax/jms/BytesMessage.java new file mode 100644 index 000000000..872caccb8 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/BytesMessage.java @@ -0,0 +1,92 @@ +package javax.jms; + +public abstract interface BytesMessage + extends Message +{ + public abstract long getBodyLength() + throws JMSException; + + public abstract boolean readBoolean() + throws JMSException; + + public abstract byte readByte() + throws JMSException; + + public abstract int readUnsignedByte() + throws JMSException; + + public abstract short readShort() + throws JMSException; + + public abstract int readUnsignedShort() + throws JMSException; + + public abstract char readChar() + throws JMSException; + + public abstract int readInt() + throws JMSException; + + public abstract long readLong() + throws JMSException; + + public abstract float readFloat() + throws JMSException; + + public abstract double readDouble() + throws JMSException; + + public abstract String readUTF() + throws JMSException; + + public abstract int readBytes(byte[] paramArrayOfByte) + throws JMSException; + + public abstract int readBytes(byte[] paramArrayOfByte, int paramInt) + throws JMSException; + + public abstract void writeBoolean(boolean paramBoolean) + throws JMSException; + + public abstract void writeByte(byte paramByte) + throws JMSException; + + public abstract void writeShort(short paramShort) + throws JMSException; + + public abstract void writeChar(char paramChar) + throws JMSException; + + public abstract void writeInt(int paramInt) + throws JMSException; + + public abstract void writeLong(long paramLong) + throws JMSException; + + public abstract void writeFloat(float paramFloat) + throws JMSException; + + public abstract void writeDouble(double paramDouble) + throws JMSException; + + public abstract void writeUTF(String paramString) + throws JMSException; + + public abstract void writeBytes(byte[] paramArrayOfByte) + throws JMSException; + + public abstract void writeBytes(byte[] paramArrayOfByte, int paramInt1, int paramInt2) + throws JMSException; + + public abstract void writeObject(Object paramObject) + throws JMSException; + + public abstract void reset() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\BytesMessage.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/Connection.java b/fine-third-default/fine-javax-jms/src/javax/jms/Connection.java new file mode 100644 index 000000000..04c0e0ed2 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/Connection.java @@ -0,0 +1,43 @@ +package javax.jms; + +public abstract interface Connection +{ + public abstract Session createSession(boolean paramBoolean, int paramInt) + throws JMSException; + + public abstract String getClientID() + throws JMSException; + + public abstract void setClientID(String paramString) + throws JMSException; + + public abstract ConnectionMetaData getMetaData() + throws JMSException; + + public abstract ExceptionListener getExceptionListener() + throws JMSException; + + public abstract void setExceptionListener(ExceptionListener paramExceptionListener) + throws JMSException; + + public abstract void start() + throws JMSException; + + public abstract void stop() + throws JMSException; + + public abstract void close() + throws JMSException; + + public abstract ConnectionConsumer createConnectionConsumer(Destination paramDestination, String paramString, ServerSessionPool paramServerSessionPool, int paramInt) + throws JMSException; + + public abstract ConnectionConsumer createDurableConnectionConsumer(Topic paramTopic, String paramString1, String paramString2, ServerSessionPool paramServerSessionPool, int paramInt) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\Connection.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/ConnectionConsumer.java b/fine-third-default/fine-javax-jms/src/javax/jms/ConnectionConsumer.java new file mode 100644 index 000000000..fe8e844bf --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/ConnectionConsumer.java @@ -0,0 +1,16 @@ +package javax.jms; + +public abstract interface ConnectionConsumer +{ + public abstract ServerSessionPool getServerSessionPool() + throws JMSException; + + public abstract void close() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\ConnectionConsumer.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/ConnectionFactory.java b/fine-third-default/fine-javax-jms/src/javax/jms/ConnectionFactory.java new file mode 100644 index 000000000..63a8390e8 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/ConnectionFactory.java @@ -0,0 +1,16 @@ +package javax.jms; + +public abstract interface ConnectionFactory +{ + public abstract Connection createConnection() + throws JMSException; + + public abstract Connection createConnection(String paramString1, String paramString2) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\ConnectionFactory.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/ConnectionMetaData.java b/fine-third-default/fine-javax-jms/src/javax/jms/ConnectionMetaData.java new file mode 100644 index 000000000..f35c7990d --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/ConnectionMetaData.java @@ -0,0 +1,36 @@ +package javax.jms; + +import java.util.Enumeration; + +public abstract interface ConnectionMetaData +{ + public abstract String getJMSVersion() + throws JMSException; + + public abstract int getJMSMajorVersion() + throws JMSException; + + public abstract int getJMSMinorVersion() + throws JMSException; + + public abstract String getJMSProviderName() + throws JMSException; + + public abstract String getProviderVersion() + throws JMSException; + + public abstract int getProviderMajorVersion() + throws JMSException; + + public abstract int getProviderMinorVersion() + throws JMSException; + + public abstract Enumeration getJMSXPropertyNames() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\ConnectionMetaData.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/DeliveryMode.java b/fine-third-default/fine-javax-jms/src/javax/jms/DeliveryMode.java new file mode 100644 index 000000000..6fd31f7d2 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/DeliveryMode.java @@ -0,0 +1,13 @@ +package javax.jms; + +public abstract interface DeliveryMode +{ + public static final int NON_PERSISTENT = 1; + public static final int PERSISTENT = 2; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\DeliveryMode.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/Destination.java b/fine-third-default/fine-javax-jms/src/javax/jms/Destination.java new file mode 100644 index 000000000..c65219fd9 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/Destination.java @@ -0,0 +1,9 @@ +package javax.jms; + +public abstract interface Destination {} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\Destination.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/ExceptionListener.java b/fine-third-default/fine-javax-jms/src/javax/jms/ExceptionListener.java new file mode 100644 index 000000000..d21139933 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/ExceptionListener.java @@ -0,0 +1,12 @@ +package javax.jms; + +public abstract interface ExceptionListener +{ + public abstract void onException(JMSException paramJMSException); +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\ExceptionListener.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/IllegalStateException.java b/fine-third-default/fine-javax-jms/src/javax/jms/IllegalStateException.java new file mode 100644 index 000000000..6f73b2db9 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/IllegalStateException.java @@ -0,0 +1,53 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class IllegalStateException +/* */ extends JMSException +/* */ { +/* */ public IllegalStateException(String reason, String errorCode) +/* */ { +/* 35 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public IllegalStateException(String reason) +/* */ { +/* 45 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\IllegalStateException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/InvalidClientIDException.java b/fine-third-default/fine-javax-jms/src/javax/jms/InvalidClientIDException.java new file mode 100644 index 000000000..a39759379 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/InvalidClientIDException.java @@ -0,0 +1,78 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class InvalidClientIDException +/* */ extends JMSException +/* */ { +/* */ public InvalidClientIDException(String reason, String errorCode) +/* */ { +/* 60 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public InvalidClientIDException(String reason) +/* */ { +/* 70 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\InvalidClientIDException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/InvalidDestinationException.java b/fine-third-default/fine-javax-jms/src/javax/jms/InvalidDestinationException.java new file mode 100644 index 000000000..28b56388b --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/InvalidDestinationException.java @@ -0,0 +1,79 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class InvalidDestinationException +/* */ extends JMSException +/* */ { +/* */ public InvalidDestinationException(String reason, String errorCode) +/* */ { +/* 61 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public InvalidDestinationException(String reason) +/* */ { +/* 71 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\InvalidDestinationException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/InvalidSelectorException.java b/fine-third-default/fine-javax-jms/src/javax/jms/InvalidSelectorException.java new file mode 100644 index 000000000..4fe64beef --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/InvalidSelectorException.java @@ -0,0 +1,79 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class InvalidSelectorException +/* */ extends JMSException +/* */ { +/* */ public InvalidSelectorException(String reason, String errorCode) +/* */ { +/* 61 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public InvalidSelectorException(String reason) +/* */ { +/* 71 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\InvalidSelectorException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/JMSException.java b/fine-third-default/fine-javax-jms/src/javax/jms/JMSException.java new file mode 100644 index 000000000..2b64952e2 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/JMSException.java @@ -0,0 +1,129 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class JMSException +/* */ extends Exception +/* */ { +/* */ private String errorCode; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ private Exception linkedException; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public JMSException(String reason, String errorCode) +/* */ { +/* 78 */ super(reason); +/* 79 */ this.errorCode = errorCode; +/* 80 */ this.linkedException = null; +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public JMSException(String reason) +/* */ { +/* 90 */ super(reason); +/* 91 */ this.errorCode = null; +/* 92 */ this.linkedException = null; +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public String getErrorCode() +/* */ { +/* 101 */ return this.errorCode; +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public Exception getLinkedException() +/* */ { +/* 111 */ return this.linkedException; +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public synchronized void setLinkedException(Exception ex) +/* */ { +/* 121 */ this.linkedException = ex; +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\JMSException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/JMSSecurityException.java b/fine-third-default/fine-javax-jms/src/javax/jms/JMSSecurityException.java new file mode 100644 index 000000000..33c03b236 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/JMSSecurityException.java @@ -0,0 +1,79 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class JMSSecurityException +/* */ extends JMSException +/* */ { +/* */ public JMSSecurityException(String reason, String errorCode) +/* */ { +/* 61 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public JMSSecurityException(String reason) +/* */ { +/* 71 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\JMSSecurityException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/MapMessage.java b/fine-third-default/fine-javax-jms/src/javax/jms/MapMessage.java new file mode 100644 index 000000000..50ef0af7c --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/MapMessage.java @@ -0,0 +1,88 @@ +package javax.jms; + +import java.util.Enumeration; + +public abstract interface MapMessage + extends Message +{ + public abstract boolean getBoolean(String paramString) + throws JMSException; + + public abstract byte getByte(String paramString) + throws JMSException; + + public abstract short getShort(String paramString) + throws JMSException; + + public abstract char getChar(String paramString) + throws JMSException; + + public abstract int getInt(String paramString) + throws JMSException; + + public abstract long getLong(String paramString) + throws JMSException; + + public abstract float getFloat(String paramString) + throws JMSException; + + public abstract double getDouble(String paramString) + throws JMSException; + + public abstract String getString(String paramString) + throws JMSException; + + public abstract byte[] getBytes(String paramString) + throws JMSException; + + public abstract Object getObject(String paramString) + throws JMSException; + + public abstract Enumeration getMapNames() + throws JMSException; + + public abstract void setBoolean(String paramString, boolean paramBoolean) + throws JMSException; + + public abstract void setByte(String paramString, byte paramByte) + throws JMSException; + + public abstract void setShort(String paramString, short paramShort) + throws JMSException; + + public abstract void setChar(String paramString, char paramChar) + throws JMSException; + + public abstract void setInt(String paramString, int paramInt) + throws JMSException; + + public abstract void setLong(String paramString, long paramLong) + throws JMSException; + + public abstract void setFloat(String paramString, float paramFloat) + throws JMSException; + + public abstract void setDouble(String paramString, double paramDouble) + throws JMSException; + + public abstract void setString(String paramString1, String paramString2) + throws JMSException; + + public abstract void setBytes(String paramString, byte[] paramArrayOfByte) + throws JMSException; + + public abstract void setBytes(String paramString, byte[] paramArrayOfByte, int paramInt1, int paramInt2) + throws JMSException; + + public abstract void setObject(String paramString, Object paramObject) + throws JMSException; + + public abstract boolean itemExists(String paramString) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\MapMessage.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/Message.java b/fine-third-default/fine-javax-jms/src/javax/jms/Message.java new file mode 100644 index 000000000..567bbd3e0 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/Message.java @@ -0,0 +1,151 @@ +package javax.jms; + +import java.util.Enumeration; + +public abstract interface Message +{ + public static final int DEFAULT_DELIVERY_MODE = 2; + public static final int DEFAULT_PRIORITY = 4; + public static final long DEFAULT_TIME_TO_LIVE = 0L; + + public abstract String getJMSMessageID() + throws JMSException; + + public abstract void setJMSMessageID(String paramString) + throws JMSException; + + public abstract long getJMSTimestamp() + throws JMSException; + + public abstract void setJMSTimestamp(long paramLong) + throws JMSException; + + public abstract byte[] getJMSCorrelationIDAsBytes() + throws JMSException; + + public abstract void setJMSCorrelationIDAsBytes(byte[] paramArrayOfByte) + throws JMSException; + + public abstract void setJMSCorrelationID(String paramString) + throws JMSException; + + public abstract String getJMSCorrelationID() + throws JMSException; + + public abstract Destination getJMSReplyTo() + throws JMSException; + + public abstract void setJMSReplyTo(Destination paramDestination) + throws JMSException; + + public abstract Destination getJMSDestination() + throws JMSException; + + public abstract void setJMSDestination(Destination paramDestination) + throws JMSException; + + public abstract int getJMSDeliveryMode() + throws JMSException; + + public abstract void setJMSDeliveryMode(int paramInt) + throws JMSException; + + public abstract boolean getJMSRedelivered() + throws JMSException; + + public abstract void setJMSRedelivered(boolean paramBoolean) + throws JMSException; + + public abstract String getJMSType() + throws JMSException; + + public abstract void setJMSType(String paramString) + throws JMSException; + + public abstract long getJMSExpiration() + throws JMSException; + + public abstract void setJMSExpiration(long paramLong) + throws JMSException; + + public abstract int getJMSPriority() + throws JMSException; + + public abstract void setJMSPriority(int paramInt) + throws JMSException; + + public abstract void clearProperties() + throws JMSException; + + public abstract boolean propertyExists(String paramString) + throws JMSException; + + public abstract boolean getBooleanProperty(String paramString) + throws JMSException; + + public abstract byte getByteProperty(String paramString) + throws JMSException; + + public abstract short getShortProperty(String paramString) + throws JMSException; + + public abstract int getIntProperty(String paramString) + throws JMSException; + + public abstract long getLongProperty(String paramString) + throws JMSException; + + public abstract float getFloatProperty(String paramString) + throws JMSException; + + public abstract double getDoubleProperty(String paramString) + throws JMSException; + + public abstract String getStringProperty(String paramString) + throws JMSException; + + public abstract Object getObjectProperty(String paramString) + throws JMSException; + + public abstract Enumeration getPropertyNames() + throws JMSException; + + public abstract void setBooleanProperty(String paramString, boolean paramBoolean) + throws JMSException; + + public abstract void setByteProperty(String paramString, byte paramByte) + throws JMSException; + + public abstract void setShortProperty(String paramString, short paramShort) + throws JMSException; + + public abstract void setIntProperty(String paramString, int paramInt) + throws JMSException; + + public abstract void setLongProperty(String paramString, long paramLong) + throws JMSException; + + public abstract void setFloatProperty(String paramString, float paramFloat) + throws JMSException; + + public abstract void setDoubleProperty(String paramString, double paramDouble) + throws JMSException; + + public abstract void setStringProperty(String paramString1, String paramString2) + throws JMSException; + + public abstract void setObjectProperty(String paramString, Object paramObject) + throws JMSException; + + public abstract void acknowledge() + throws JMSException; + + public abstract void clearBody() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\Message.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/MessageConsumer.java b/fine-third-default/fine-javax-jms/src/javax/jms/MessageConsumer.java new file mode 100644 index 000000000..c6f68a19e --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/MessageConsumer.java @@ -0,0 +1,31 @@ +package javax.jms; + +public abstract interface MessageConsumer +{ + public abstract String getMessageSelector() + throws JMSException; + + public abstract MessageListener getMessageListener() + throws JMSException; + + public abstract void setMessageListener(MessageListener paramMessageListener) + throws JMSException; + + public abstract Message receive() + throws JMSException; + + public abstract Message receive(long paramLong) + throws JMSException; + + public abstract Message receiveNoWait() + throws JMSException; + + public abstract void close() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\MessageConsumer.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/MessageEOFException.java b/fine-third-default/fine-javax-jms/src/javax/jms/MessageEOFException.java new file mode 100644 index 000000000..c9c8b21a1 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/MessageEOFException.java @@ -0,0 +1,79 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class MessageEOFException +/* */ extends JMSException +/* */ { +/* */ public MessageEOFException(String reason, String errorCode) +/* */ { +/* 61 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public MessageEOFException(String reason) +/* */ { +/* 71 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\MessageEOFException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/MessageFormatException.java b/fine-third-default/fine-javax-jms/src/javax/jms/MessageFormatException.java new file mode 100644 index 000000000..389706f7d --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/MessageFormatException.java @@ -0,0 +1,87 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class MessageFormatException +/* */ extends JMSException +/* */ { +/* */ public MessageFormatException(String reason, String errorCode) +/* */ { +/* 69 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public MessageFormatException(String reason) +/* */ { +/* 79 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\MessageFormatException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/MessageListener.java b/fine-third-default/fine-javax-jms/src/javax/jms/MessageListener.java new file mode 100644 index 000000000..bd6fc05fa --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/MessageListener.java @@ -0,0 +1,12 @@ +package javax.jms; + +public abstract interface MessageListener +{ + public abstract void onMessage(Message paramMessage); +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\MessageListener.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/MessageNotReadableException.java b/fine-third-default/fine-javax-jms/src/javax/jms/MessageNotReadableException.java new file mode 100644 index 000000000..0d281281b --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/MessageNotReadableException.java @@ -0,0 +1,78 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class MessageNotReadableException +/* */ extends JMSException +/* */ { +/* */ public MessageNotReadableException(String reason, String errorCode) +/* */ { +/* 60 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public MessageNotReadableException(String reason) +/* */ { +/* 70 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\MessageNotReadableException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/MessageNotWriteableException.java b/fine-third-default/fine-javax-jms/src/javax/jms/MessageNotWriteableException.java new file mode 100644 index 000000000..e1d848937 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/MessageNotWriteableException.java @@ -0,0 +1,78 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class MessageNotWriteableException +/* */ extends JMSException +/* */ { +/* */ public MessageNotWriteableException(String reason, String errorCode) +/* */ { +/* 60 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public MessageNotWriteableException(String reason) +/* */ { +/* 70 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\MessageNotWriteableException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/MessageProducer.java b/fine-third-default/fine-javax-jms/src/javax/jms/MessageProducer.java new file mode 100644 index 000000000..24ed1b9e1 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/MessageProducer.java @@ -0,0 +1,58 @@ +package javax.jms; + +public abstract interface MessageProducer +{ + public abstract void setDisableMessageID(boolean paramBoolean) + throws JMSException; + + public abstract boolean getDisableMessageID() + throws JMSException; + + public abstract void setDisableMessageTimestamp(boolean paramBoolean) + throws JMSException; + + public abstract boolean getDisableMessageTimestamp() + throws JMSException; + + public abstract void setDeliveryMode(int paramInt) + throws JMSException; + + public abstract int getDeliveryMode() + throws JMSException; + + public abstract void setPriority(int paramInt) + throws JMSException; + + public abstract int getPriority() + throws JMSException; + + public abstract void setTimeToLive(long paramLong) + throws JMSException; + + public abstract long getTimeToLive() + throws JMSException; + + public abstract Destination getDestination() + throws JMSException; + + public abstract void close() + throws JMSException; + + public abstract void send(Message paramMessage) + throws JMSException; + + public abstract void send(Message paramMessage, int paramInt1, int paramInt2, long paramLong) + throws JMSException; + + public abstract void send(Destination paramDestination, Message paramMessage) + throws JMSException; + + public abstract void send(Destination paramDestination, Message paramMessage, int paramInt1, int paramInt2, long paramLong) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\MessageProducer.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/ObjectMessage.java b/fine-third-default/fine-javax-jms/src/javax/jms/ObjectMessage.java new file mode 100644 index 000000000..73209b3bc --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/ObjectMessage.java @@ -0,0 +1,19 @@ +package javax.jms; + +import java.io.Serializable; + +public abstract interface ObjectMessage + extends Message +{ + public abstract void setObject(Serializable paramSerializable) + throws JMSException; + + public abstract Serializable getObject() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\ObjectMessage.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/Queue.java b/fine-third-default/fine-javax-jms/src/javax/jms/Queue.java new file mode 100644 index 000000000..e5473b90d --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/Queue.java @@ -0,0 +1,16 @@ +package javax.jms; + +public abstract interface Queue + extends Destination +{ + public abstract String getQueueName() + throws JMSException; + + public abstract String toString(); +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\Queue.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/QueueBrowser.java b/fine-third-default/fine-javax-jms/src/javax/jms/QueueBrowser.java new file mode 100644 index 000000000..ab3eb92ea --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/QueueBrowser.java @@ -0,0 +1,24 @@ +package javax.jms; + +import java.util.Enumeration; + +public abstract interface QueueBrowser +{ + public abstract Queue getQueue() + throws JMSException; + + public abstract String getMessageSelector() + throws JMSException; + + public abstract Enumeration getEnumeration() + throws JMSException; + + public abstract void close() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\QueueBrowser.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/QueueConnection.java b/fine-third-default/fine-javax-jms/src/javax/jms/QueueConnection.java new file mode 100644 index 000000000..4b0e33455 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/QueueConnection.java @@ -0,0 +1,17 @@ +package javax.jms; + +public abstract interface QueueConnection + extends Connection +{ + public abstract QueueSession createQueueSession(boolean paramBoolean, int paramInt) + throws JMSException; + + public abstract ConnectionConsumer createConnectionConsumer(Queue paramQueue, String paramString, ServerSessionPool paramServerSessionPool, int paramInt) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\QueueConnection.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/QueueConnectionFactory.java b/fine-third-default/fine-javax-jms/src/javax/jms/QueueConnectionFactory.java new file mode 100644 index 000000000..02e6ceed9 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/QueueConnectionFactory.java @@ -0,0 +1,17 @@ +package javax.jms; + +public abstract interface QueueConnectionFactory + extends ConnectionFactory +{ + public abstract QueueConnection createQueueConnection() + throws JMSException; + + public abstract QueueConnection createQueueConnection(String paramString1, String paramString2) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\QueueConnectionFactory.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/QueueReceiver.java b/fine-third-default/fine-javax-jms/src/javax/jms/QueueReceiver.java new file mode 100644 index 000000000..a7bdbb027 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/QueueReceiver.java @@ -0,0 +1,14 @@ +package javax.jms; + +public abstract interface QueueReceiver + extends MessageConsumer +{ + public abstract Queue getQueue() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\QueueReceiver.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/QueueRequestor.java b/fine-third-default/fine-javax-jms/src/javax/jms/QueueRequestor.java new file mode 100644 index 000000000..c3706bf36 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/QueueRequestor.java @@ -0,0 +1,143 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class QueueRequestor +/* */ { +/* */ QueueSession session; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ Queue queue; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ TemporaryQueue tempQueue; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ QueueSender sender; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ QueueReceiver receiver; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public QueueRequestor(QueueSession session, Queue queue) +/* */ throws JMSException +/* */ { +/* 86 */ this.session = session; +/* 87 */ this.queue = queue; +/* 88 */ this.tempQueue = session.createTemporaryQueue(); +/* 89 */ this.sender = session.createSender(queue); +/* 90 */ this.receiver = session.createReceiver(this.tempQueue); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public Message request(Message message) +/* */ throws JMSException +/* */ { +/* 108 */ message.setJMSReplyTo(this.tempQueue); +/* 109 */ this.sender.send(message); +/* 110 */ return this.receiver.receive(); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public void close() +/* */ throws JMSException +/* */ { +/* 134 */ this.session.close(); +/* 135 */ this.tempQueue.delete(); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\QueueRequestor.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/QueueSender.java b/fine-third-default/fine-javax-jms/src/javax/jms/QueueSender.java new file mode 100644 index 000000000..850f76f1d --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/QueueSender.java @@ -0,0 +1,26 @@ +package javax.jms; + +public abstract interface QueueSender + extends MessageProducer +{ + public abstract Queue getQueue() + throws JMSException; + + public abstract void send(Message paramMessage) + throws JMSException; + + public abstract void send(Message paramMessage, int paramInt1, int paramInt2, long paramLong) + throws JMSException; + + public abstract void send(Queue paramQueue, Message paramMessage) + throws JMSException; + + public abstract void send(Queue paramQueue, Message paramMessage, int paramInt1, int paramInt2, long paramLong) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\QueueSender.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/QueueSession.java b/fine-third-default/fine-javax-jms/src/javax/jms/QueueSession.java new file mode 100644 index 000000000..0b9e616d7 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/QueueSession.java @@ -0,0 +1,32 @@ +package javax.jms; + +public abstract interface QueueSession + extends Session +{ + public abstract Queue createQueue(String paramString) + throws JMSException; + + public abstract QueueReceiver createReceiver(Queue paramQueue) + throws JMSException; + + public abstract QueueReceiver createReceiver(Queue paramQueue, String paramString) + throws JMSException; + + public abstract QueueSender createSender(Queue paramQueue) + throws JMSException; + + public abstract QueueBrowser createBrowser(Queue paramQueue) + throws JMSException; + + public abstract QueueBrowser createBrowser(Queue paramQueue, String paramString) + throws JMSException; + + public abstract TemporaryQueue createTemporaryQueue() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\QueueSession.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/ResourceAllocationException.java b/fine-third-default/fine-javax-jms/src/javax/jms/ResourceAllocationException.java new file mode 100644 index 000000000..31c55a62b --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/ResourceAllocationException.java @@ -0,0 +1,81 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class ResourceAllocationException +/* */ extends JMSException +/* */ { +/* */ public ResourceAllocationException(String reason, String errorCode) +/* */ { +/* 63 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public ResourceAllocationException(String reason) +/* */ { +/* 73 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\ResourceAllocationException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/ServerSession.java b/fine-third-default/fine-javax-jms/src/javax/jms/ServerSession.java new file mode 100644 index 000000000..333f529b6 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/ServerSession.java @@ -0,0 +1,16 @@ +package javax.jms; + +public abstract interface ServerSession +{ + public abstract Session getSession() + throws JMSException; + + public abstract void start() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\ServerSession.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/ServerSessionPool.java b/fine-third-default/fine-javax-jms/src/javax/jms/ServerSessionPool.java new file mode 100644 index 000000000..a90c23b6f --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/ServerSessionPool.java @@ -0,0 +1,13 @@ +package javax.jms; + +public abstract interface ServerSessionPool +{ + public abstract ServerSession getServerSession() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\ServerSessionPool.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/Session.java b/fine-third-default/fine-javax-jms/src/javax/jms/Session.java new file mode 100644 index 000000000..1d381e230 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/Session.java @@ -0,0 +1,107 @@ +package javax.jms; + +import java.io.Serializable; + +public abstract interface Session + extends Runnable +{ + public static final int AUTO_ACKNOWLEDGE = 1; + public static final int CLIENT_ACKNOWLEDGE = 2; + public static final int DUPS_OK_ACKNOWLEDGE = 3; + public static final int SESSION_TRANSACTED = 0; + + public abstract BytesMessage createBytesMessage() + throws JMSException; + + public abstract MapMessage createMapMessage() + throws JMSException; + + public abstract Message createMessage() + throws JMSException; + + public abstract ObjectMessage createObjectMessage() + throws JMSException; + + public abstract ObjectMessage createObjectMessage(Serializable paramSerializable) + throws JMSException; + + public abstract StreamMessage createStreamMessage() + throws JMSException; + + public abstract TextMessage createTextMessage() + throws JMSException; + + public abstract TextMessage createTextMessage(String paramString) + throws JMSException; + + public abstract boolean getTransacted() + throws JMSException; + + public abstract int getAcknowledgeMode() + throws JMSException; + + public abstract void commit() + throws JMSException; + + public abstract void rollback() + throws JMSException; + + public abstract void close() + throws JMSException; + + public abstract void recover() + throws JMSException; + + public abstract MessageListener getMessageListener() + throws JMSException; + + public abstract void setMessageListener(MessageListener paramMessageListener) + throws JMSException; + + public abstract void run(); + + public abstract MessageProducer createProducer(Destination paramDestination) + throws JMSException; + + public abstract MessageConsumer createConsumer(Destination paramDestination) + throws JMSException; + + public abstract MessageConsumer createConsumer(Destination paramDestination, String paramString) + throws JMSException; + + public abstract MessageConsumer createConsumer(Destination paramDestination, String paramString, boolean paramBoolean) + throws JMSException; + + public abstract Queue createQueue(String paramString) + throws JMSException; + + public abstract Topic createTopic(String paramString) + throws JMSException; + + public abstract TopicSubscriber createDurableSubscriber(Topic paramTopic, String paramString) + throws JMSException; + + public abstract TopicSubscriber createDurableSubscriber(Topic paramTopic, String paramString1, String paramString2, boolean paramBoolean) + throws JMSException; + + public abstract QueueBrowser createBrowser(Queue paramQueue) + throws JMSException; + + public abstract QueueBrowser createBrowser(Queue paramQueue, String paramString) + throws JMSException; + + public abstract TemporaryQueue createTemporaryQueue() + throws JMSException; + + public abstract TemporaryTopic createTemporaryTopic() + throws JMSException; + + public abstract void unsubscribe(String paramString) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\Session.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/StreamMessage.java b/fine-third-default/fine-javax-jms/src/javax/jms/StreamMessage.java new file mode 100644 index 000000000..7cdc7d24c --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/StreamMessage.java @@ -0,0 +1,83 @@ +package javax.jms; + +public abstract interface StreamMessage + extends Message +{ + public abstract boolean readBoolean() + throws JMSException; + + public abstract byte readByte() + throws JMSException; + + public abstract short readShort() + throws JMSException; + + public abstract char readChar() + throws JMSException; + + public abstract int readInt() + throws JMSException; + + public abstract long readLong() + throws JMSException; + + public abstract float readFloat() + throws JMSException; + + public abstract double readDouble() + throws JMSException; + + public abstract String readString() + throws JMSException; + + public abstract int readBytes(byte[] paramArrayOfByte) + throws JMSException; + + public abstract Object readObject() + throws JMSException; + + public abstract void writeBoolean(boolean paramBoolean) + throws JMSException; + + public abstract void writeByte(byte paramByte) + throws JMSException; + + public abstract void writeShort(short paramShort) + throws JMSException; + + public abstract void writeChar(char paramChar) + throws JMSException; + + public abstract void writeInt(int paramInt) + throws JMSException; + + public abstract void writeLong(long paramLong) + throws JMSException; + + public abstract void writeFloat(float paramFloat) + throws JMSException; + + public abstract void writeDouble(double paramDouble) + throws JMSException; + + public abstract void writeString(String paramString) + throws JMSException; + + public abstract void writeBytes(byte[] paramArrayOfByte) + throws JMSException; + + public abstract void writeBytes(byte[] paramArrayOfByte, int paramInt1, int paramInt2) + throws JMSException; + + public abstract void writeObject(Object paramObject) + throws JMSException; + + public abstract void reset() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\StreamMessage.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TemporaryQueue.java b/fine-third-default/fine-javax-jms/src/javax/jms/TemporaryQueue.java new file mode 100644 index 000000000..226624943 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TemporaryQueue.java @@ -0,0 +1,14 @@ +package javax.jms; + +public abstract interface TemporaryQueue + extends Queue +{ + public abstract void delete() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TemporaryQueue.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TemporaryTopic.java b/fine-third-default/fine-javax-jms/src/javax/jms/TemporaryTopic.java new file mode 100644 index 000000000..e162e13e9 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TemporaryTopic.java @@ -0,0 +1,14 @@ +package javax.jms; + +public abstract interface TemporaryTopic + extends Topic +{ + public abstract void delete() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TemporaryTopic.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TextMessage.java b/fine-third-default/fine-javax-jms/src/javax/jms/TextMessage.java new file mode 100644 index 000000000..50fbc1dc2 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TextMessage.java @@ -0,0 +1,17 @@ +package javax.jms; + +public abstract interface TextMessage + extends Message +{ + public abstract void setText(String paramString) + throws JMSException; + + public abstract String getText() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TextMessage.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/Topic.java b/fine-third-default/fine-javax-jms/src/javax/jms/Topic.java new file mode 100644 index 000000000..10c0671ac --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/Topic.java @@ -0,0 +1,16 @@ +package javax.jms; + +public abstract interface Topic + extends Destination +{ + public abstract String getTopicName() + throws JMSException; + + public abstract String toString(); +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\Topic.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TopicConnection.java b/fine-third-default/fine-javax-jms/src/javax/jms/TopicConnection.java new file mode 100644 index 000000000..d06ec89f8 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TopicConnection.java @@ -0,0 +1,20 @@ +package javax.jms; + +public abstract interface TopicConnection + extends Connection +{ + public abstract TopicSession createTopicSession(boolean paramBoolean, int paramInt) + throws JMSException; + + public abstract ConnectionConsumer createConnectionConsumer(Topic paramTopic, String paramString, ServerSessionPool paramServerSessionPool, int paramInt) + throws JMSException; + + public abstract ConnectionConsumer createDurableConnectionConsumer(Topic paramTopic, String paramString1, String paramString2, ServerSessionPool paramServerSessionPool, int paramInt) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TopicConnection.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TopicConnectionFactory.java b/fine-third-default/fine-javax-jms/src/javax/jms/TopicConnectionFactory.java new file mode 100644 index 000000000..02c399ebf --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TopicConnectionFactory.java @@ -0,0 +1,17 @@ +package javax.jms; + +public abstract interface TopicConnectionFactory + extends ConnectionFactory +{ + public abstract TopicConnection createTopicConnection() + throws JMSException; + + public abstract TopicConnection createTopicConnection(String paramString1, String paramString2) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TopicConnectionFactory.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TopicPublisher.java b/fine-third-default/fine-javax-jms/src/javax/jms/TopicPublisher.java new file mode 100644 index 000000000..c695b6577 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TopicPublisher.java @@ -0,0 +1,26 @@ +package javax.jms; + +public abstract interface TopicPublisher + extends MessageProducer +{ + public abstract Topic getTopic() + throws JMSException; + + public abstract void publish(Message paramMessage) + throws JMSException; + + public abstract void publish(Message paramMessage, int paramInt1, int paramInt2, long paramLong) + throws JMSException; + + public abstract void publish(Topic paramTopic, Message paramMessage) + throws JMSException; + + public abstract void publish(Topic paramTopic, Message paramMessage, int paramInt1, int paramInt2, long paramLong) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TopicPublisher.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TopicRequestor.java b/fine-third-default/fine-javax-jms/src/javax/jms/TopicRequestor.java new file mode 100644 index 000000000..fdda4bc7c --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TopicRequestor.java @@ -0,0 +1,142 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class TopicRequestor +/* */ { +/* */ TopicSession session; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ Topic topic; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ TemporaryTopic tempTopic; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ TopicPublisher publisher; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ TopicSubscriber subscriber; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public TopicRequestor(TopicSession session, Topic topic) +/* */ throws JMSException +/* */ { +/* 85 */ this.session = session; +/* 86 */ this.topic = topic; +/* 87 */ this.tempTopic = session.createTemporaryTopic(); +/* 88 */ this.publisher = session.createPublisher(topic); +/* 89 */ this.subscriber = session.createSubscriber(this.tempTopic); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public Message request(Message message) +/* */ throws JMSException +/* */ { +/* 107 */ message.setJMSReplyTo(this.tempTopic); +/* 108 */ this.publisher.publish(message); +/* 109 */ return this.subscriber.receive(); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public void close() +/* */ throws JMSException +/* */ { +/* 133 */ this.session.close(); +/* 134 */ this.tempTopic.delete(); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TopicRequestor.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TopicSession.java b/fine-third-default/fine-javax-jms/src/javax/jms/TopicSession.java new file mode 100644 index 000000000..2c8a29c39 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TopicSession.java @@ -0,0 +1,35 @@ +package javax.jms; + +public abstract interface TopicSession + extends Session +{ + public abstract Topic createTopic(String paramString) + throws JMSException; + + public abstract TopicSubscriber createSubscriber(Topic paramTopic) + throws JMSException; + + public abstract TopicSubscriber createSubscriber(Topic paramTopic, String paramString, boolean paramBoolean) + throws JMSException; + + public abstract TopicSubscriber createDurableSubscriber(Topic paramTopic, String paramString) + throws JMSException; + + public abstract TopicSubscriber createDurableSubscriber(Topic paramTopic, String paramString1, String paramString2, boolean paramBoolean) + throws JMSException; + + public abstract TopicPublisher createPublisher(Topic paramTopic) + throws JMSException; + + public abstract TemporaryTopic createTemporaryTopic() + throws JMSException; + + public abstract void unsubscribe(String paramString) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TopicSession.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TopicSubscriber.java b/fine-third-default/fine-javax-jms/src/javax/jms/TopicSubscriber.java new file mode 100644 index 000000000..dd5469c42 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TopicSubscriber.java @@ -0,0 +1,17 @@ +package javax.jms; + +public abstract interface TopicSubscriber + extends MessageConsumer +{ + public abstract Topic getTopic() + throws JMSException; + + public abstract boolean getNoLocal() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TopicSubscriber.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TransactionInProgressException.java b/fine-third-default/fine-javax-jms/src/javax/jms/TransactionInProgressException.java new file mode 100644 index 000000000..e40f1aa9b --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TransactionInProgressException.java @@ -0,0 +1,80 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class TransactionInProgressException +/* */ extends JMSException +/* */ { +/* */ public TransactionInProgressException(String reason, String errorCode) +/* */ { +/* 62 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public TransactionInProgressException(String reason) +/* */ { +/* 72 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TransactionInProgressException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/TransactionRolledBackException.java b/fine-third-default/fine-javax-jms/src/javax/jms/TransactionRolledBackException.java new file mode 100644 index 000000000..a39661fbf --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/TransactionRolledBackException.java @@ -0,0 +1,78 @@ +/* */ package javax.jms; +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public class TransactionRolledBackException +/* */ extends JMSException +/* */ { +/* */ public TransactionRolledBackException(String reason, String errorCode) +/* */ { +/* 60 */ super(reason, errorCode); +/* */ } +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ +/* */ public TransactionRolledBackException(String reason) +/* */ { +/* 70 */ super(reason); +/* */ } +/* */ } + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\TransactionRolledBackException.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/XAConnection.java b/fine-third-default/fine-javax-jms/src/javax/jms/XAConnection.java new file mode 100644 index 000000000..05af290e4 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/XAConnection.java @@ -0,0 +1,17 @@ +package javax.jms; + +public abstract interface XAConnection + extends Connection +{ + public abstract XASession createXASession() + throws JMSException; + + public abstract Session createSession(boolean paramBoolean, int paramInt) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\XAConnection.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/XAConnectionFactory.java b/fine-third-default/fine-javax-jms/src/javax/jms/XAConnectionFactory.java new file mode 100644 index 000000000..149708f75 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/XAConnectionFactory.java @@ -0,0 +1,16 @@ +package javax.jms; + +public abstract interface XAConnectionFactory +{ + public abstract XAConnection createXAConnection() + throws JMSException; + + public abstract XAConnection createXAConnection(String paramString1, String paramString2) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\XAConnectionFactory.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/XAQueueConnection.java b/fine-third-default/fine-javax-jms/src/javax/jms/XAQueueConnection.java new file mode 100644 index 000000000..b5cd42f3c --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/XAQueueConnection.java @@ -0,0 +1,17 @@ +package javax.jms; + +public abstract interface XAQueueConnection + extends XAConnection, QueueConnection +{ + public abstract XAQueueSession createXAQueueSession() + throws JMSException; + + public abstract QueueSession createQueueSession(boolean paramBoolean, int paramInt) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\XAQueueConnection.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/XAQueueConnectionFactory.java b/fine-third-default/fine-javax-jms/src/javax/jms/XAQueueConnectionFactory.java new file mode 100644 index 000000000..bf8cb0621 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/XAQueueConnectionFactory.java @@ -0,0 +1,17 @@ +package javax.jms; + +public abstract interface XAQueueConnectionFactory + extends XAConnectionFactory, QueueConnectionFactory +{ + public abstract XAQueueConnection createXAQueueConnection() + throws JMSException; + + public abstract XAQueueConnection createXAQueueConnection(String paramString1, String paramString2) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\XAQueueConnectionFactory.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/XAQueueSession.java b/fine-third-default/fine-javax-jms/src/javax/jms/XAQueueSession.java new file mode 100644 index 000000000..229049a77 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/XAQueueSession.java @@ -0,0 +1,14 @@ +package javax.jms; + +public abstract interface XAQueueSession + extends XASession +{ + public abstract QueueSession getQueueSession() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\XAQueueSession.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/XASession.java b/fine-third-default/fine-javax-jms/src/javax/jms/XASession.java new file mode 100644 index 000000000..43a7d6aaa --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/XASession.java @@ -0,0 +1,27 @@ +package javax.jms; + +import javax.transaction.xa.XAResource; + +public abstract interface XASession + extends Session +{ + public abstract Session getSession() + throws JMSException; + + public abstract XAResource getXAResource(); + + public abstract boolean getTransacted() + throws JMSException; + + public abstract void commit() + throws JMSException; + + public abstract void rollback() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\XASession.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/XATopicConnection.java b/fine-third-default/fine-javax-jms/src/javax/jms/XATopicConnection.java new file mode 100644 index 000000000..f113333d2 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/XATopicConnection.java @@ -0,0 +1,17 @@ +package javax.jms; + +public abstract interface XATopicConnection + extends XAConnection, TopicConnection +{ + public abstract XATopicSession createXATopicSession() + throws JMSException; + + public abstract TopicSession createTopicSession(boolean paramBoolean, int paramInt) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\XATopicConnection.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/XATopicConnectionFactory.java b/fine-third-default/fine-javax-jms/src/javax/jms/XATopicConnectionFactory.java new file mode 100644 index 000000000..6f8aa3da9 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/XATopicConnectionFactory.java @@ -0,0 +1,17 @@ +package javax.jms; + +public abstract interface XATopicConnectionFactory + extends XAConnectionFactory, TopicConnectionFactory +{ + public abstract XATopicConnection createXATopicConnection() + throws JMSException; + + public abstract XATopicConnection createXATopicConnection(String paramString1, String paramString2) + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\XATopicConnectionFactory.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-jms/src/javax/jms/XATopicSession.java b/fine-third-default/fine-javax-jms/src/javax/jms/XATopicSession.java new file mode 100644 index 000000000..9956674e6 --- /dev/null +++ b/fine-third-default/fine-javax-jms/src/javax/jms/XATopicSession.java @@ -0,0 +1,14 @@ +package javax.jms; + +public abstract interface XATopicSession + extends XASession +{ + public abstract TopicSession getTopicSession() + throws JMSException; +} + + +/* Location: D:\git\basic_core_2\base-stable\core-log4j\lib\javax.jms.jar!\javax\jms\XATopicSession.class + * Java compiler version: 5 (49.0) + * JD-Core Version: 0.7.1 + */ \ No newline at end of file diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/HeuristicCommitException.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/HeuristicCommitException.java new file mode 100644 index 000000000..789310cc5 --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/HeuristicCommitException.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * This exception is thrown by the rollback operation on a resource to + * report that a heuristic decision was made and that all relevant updates + * have been committed. + */ +public class HeuristicCommitException extends java.lang.Exception +{ + /** + * Specify serialVersionUID for backward compatibility + */ + private static final long serialVersionUID = -3977609782149921760L; + + public HeuristicCommitException() + { + super(); + } + + public HeuristicCommitException(String msg) + { + super(msg); + } +} + diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/HeuristicMixedException.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/HeuristicMixedException.java new file mode 100644 index 000000000..277361e87 --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/HeuristicMixedException.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * This exception is thrown to report that a heuristic decision was made and + * that some relevant updates have been committed and others have been + * rolled back. + */ +public class HeuristicMixedException extends java.lang.Exception +{ + /** + * Specify serialVersionUID for backward compatibility + */ + private static final long serialVersionUID = 2345014349685956666L; + + public HeuristicMixedException() + { + super(); + } + + public HeuristicMixedException(String msg) + { + super(msg); + } +} + diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/HeuristicRollbackException.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/HeuristicRollbackException.java new file mode 100644 index 000000000..a08e46a3b --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/HeuristicRollbackException.java @@ -0,0 +1,64 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * This exception is thrown by the commit operation to report that a heuristic + * decision was made and that all relevant updates have been rolled back. + */ +public class HeuristicRollbackException extends java.lang.Exception +{ + /** + * Specify serialVersionUID for backward compatibility + */ + private static final long serialVersionUID = -3483618944556408897L; + + public HeuristicRollbackException() + { + super(); + } + + public HeuristicRollbackException(String msg) + { + super(msg); + } +} + diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/InvalidTransactionException.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/InvalidTransactionException.java new file mode 100644 index 000000000..dfafe3762 --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/InvalidTransactionException.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * This exception indicates that the request carried an invalid transaction + * context. For example, this exception could be raised if an error + * occurred when trying to register a resource. + */ +public class InvalidTransactionException extends java.rmi.RemoteException +{ + /** + * Specify serialVersionUID for backward compatibility + */ + private static final long serialVersionUID = 3597320220337691496L; + + public InvalidTransactionException() + { + super(); + } + + public InvalidTransactionException(String msg) + { + super(msg); + } +} + diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/NotSupportedException.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/NotSupportedException.java new file mode 100644 index 000000000..d9831a91e --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/NotSupportedException.java @@ -0,0 +1,70 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * NotSupportedException exception indicates that the request cannot be + * executed because the operation is not a supported feature. For example, + * because nested transactions are not supported, the Transaction Manager + * throws this exception when a calling thread + * that is already associated with a transaction attempts to start a new + * transaction. (A nested transaction occurs when a thread is already + * associated with one transaction and attempts to start a second + * transaction.) + */ +public class NotSupportedException extends java.lang.Exception +{ + /** + * Specify serialVersionUID for backward compatibility + */ + private static final long serialVersionUID = 56870312332816390L; + + public NotSupportedException() + { + super(); + } + + public NotSupportedException(String msg) + { + super(msg); + } +} + diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/RollbackException.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/RollbackException.java new file mode 100644 index 000000000..4801a6514 --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/RollbackException.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * RollbackException exception is thrown when the transaction has been + * marked for rollback only or the transaction has been rolled back + * instead of committed. This is a local exception thrown by methods + * in the UserTransaction, Transaction, and + * TransactionManager interfaces. + */ +public class RollbackException extends java.lang.Exception +{ + /** + * Specify serialVersionUID for backward compatibility + */ + private static final long serialVersionUID = 4151607774785285395L; + + public RollbackException() + { + super(); + } + + public RollbackException(String msg) + { + super(msg); + } +} + diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/Status.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/Status.java new file mode 100644 index 000000000..3529fab73 --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/Status.java @@ -0,0 +1,126 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * The Status interface defines static variables used for transaction + * status codes. + */ + +public interface Status { + /** + * A transaction is associated with the target object and it is in the + * active state. An implementation returns this status after a + * transaction has been started and prior to a Coordinator issuing + * any prepares, unless the transaction has been marked for rollback. + */ + public final static int STATUS_ACTIVE = 0; + + /** + * A transaction is associated with the target object and it has been + * marked for rollback, perhaps as a result of a setRollbackOnly operation. + */ + public final static int STATUS_MARKED_ROLLBACK = 1; + + /** + * A transaction is associated with the target object and it has been + * prepared. That is, all subordinates have agreed to commit. The + * target object may be waiting for instructions from a superior as to how + * to proceed. + */ + public final static int STATUS_PREPARED = 2; + + /** + * A transaction is associated with the target object and it has been + * committed. It is likely that heuristics exist; otherwise, the + * transaction would have been destroyed and NoTransaction returned. + */ + public final static int STATUS_COMMITTED = 3; + + /** + * A transaction is associated with the target object and the outcome + * has been determined to be rollback. It is likely that heuristics exist; + * otherwise, the transaction would have been destroyed and NoTransaction + * returned. + */ + public final static int STATUS_ROLLEDBACK = 4; + + /** + * A transaction is associated with the target object but its + * current status cannot be determined. This is a transient condition + * and a subsequent invocation will ultimately return a different status. + */ + public final static int STATUS_UNKNOWN = 5; + + /** + * No transaction is currently associated with the target object. This + * will occur after a transaction has completed. + */ + public final static int STATUS_NO_TRANSACTION = 6; + + /** + * A transaction is associated with the target object and it is in the + * process of preparing. An implementation returns this status if it + * has started preparing, but has not yet completed the process. The + * likely reason for this is that the implementation is probably + * waiting for responses to prepare from one or more + * Resources. + */ + public final static int STATUS_PREPARING = 7; + + /** + * A transaction is associated with the target object and it is in the + * process of committing. An implementation returns this status if it + * has decided to commit but has not yet completed the committing process. + * This occurs because the implementation is probably waiting for + * responses from one or more Resources. + */ + public final static int STATUS_COMMITTING = 8; + + /** + * A transaction is associated with the target object and it is in the + * process of rolling back. An implementation returns this status if + * it has decided to rollback but has not yet completed the process. + * The implementation is probably waiting for responses from one or more + * Resources. + */ + public final static int STATUS_ROLLING_BACK = 9; +} diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/Synchronization.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/Synchronization.java new file mode 100644 index 000000000..0f01ea41d --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/Synchronization.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * The transaction manager supports a synchronization mechanism + * that allows the interested party to be notified before and + * after the transaction completes. Using the registerSynchronization + * method, the application server registers a Synchronization object + * for the transaction currently associated with the target Transaction + * object. + */ +public interface Synchronization { + + /** + * The beforeCompletion method is called by the transaction manager prior + * to the start of the two-phase transaction commit process. This call is + * executed with the transaction context of the transaction that is being + * committed. + */ + public void beforeCompletion(); + + /** + * This method is called by the transaction + * manager after the transaction is committed or rolled back. + * + * @param status The status of the transaction completion. + */ + public void afterCompletion(int status); +} diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/SystemException.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/SystemException.java new file mode 100644 index 000000000..585149866 --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/SystemException.java @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * The SystemException is thrown by the transaction manager to + * indicate that it has encountered an unexpected error condition + * that prevents future transaction services from proceeding. + */ +public class SystemException extends java.lang.Exception { + + /** + * Specify serialVersionUID for backward compatibility + */ + private static final long serialVersionUID = 839699079412719325L; + + /** + * The error code with which to create the SystemException. + * + * @serial The error code for the exception + */ + public int errorCode; + + public SystemException() + { + super(); + } + + /** + * Create a SystemException with a given string. + * + * @param s The string message for the exception + */ + public SystemException(String s) + { + super(s); + } + + /** + * Create a SystemException with a given error code. + * + * @param errcode The error code for the exception + */ + public SystemException(int errcode) + { + super(); + errorCode = errcode; + } + + +} diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/Transaction.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/Transaction.java new file mode 100644 index 000000000..624f80cde --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/Transaction.java @@ -0,0 +1,199 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +import javax.transaction.xa.XAResource; +import java.lang.IllegalStateException; +import java.lang.SecurityException; + +/** + * The Transaction interface allows operations to be performed against + * the transaction in the target Transaction object. A Transaction + * object is created corresponding to each global transaction creation. + * The Transaction object can be used for resource enlistment, + * synchronization registration, transaction completion, and status + * query operations. + */ + +public interface Transaction { + + /** + * Complete the transaction represented by this Transaction object. + * + * @exception RollbackException Thrown to indicate that + * the transaction has been rolled back rather than committed. + * + * @exception HeuristicMixedException Thrown to indicate that a heuristic + * decision was made and that some relevant updates have been committed + * while others have been rolled back. + * + * @exception HeuristicRollbackException Thrown to indicate that a + * heuristic decision was made and that all relevant updates have been + * rolled back. + * + * @exception SecurityException Thrown to indicate that the thread is + * not allowed to commit the transaction. + * + * @exception IllegalStateException Thrown if the transaction in the + * target object is inactive. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + */ + public void commit() throws RollbackException, + HeuristicMixedException, HeuristicRollbackException, + SecurityException, IllegalStateException, SystemException; + + /** + * Disassociate the resource specified from the transaction associated + * with the target Transaction object. + * + * @param xaRes The XAResource object associated with the resource + * (connection). + * + * @param flag One of the values of TMSUCCESS, TMSUSPEND, or TMFAIL. + * + * @exception IllegalStateException Thrown if the transaction in the + * target object is inactive. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + * @return true if the resource was delisted successfully; otherwise + * false. + * + */ + public boolean delistResource(XAResource xaRes, int flag) + throws IllegalStateException, SystemException; + + /** + * Enlist the resource specified with the transaction associated with the + * target Transaction object. + * + * @param xaRes The XAResource object associated with the resource + * (connection). + * + * @return true if the resource was enlisted successfully; otherwise + * false. + * + * @exception RollbackException Thrown to indicate that + * the transaction has been marked for rollback only. + * + * @exception IllegalStateException Thrown if the transaction in the + * target object is in the prepared state or the transaction is + * inactive. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public boolean enlistResource(XAResource xaRes) + throws RollbackException, IllegalStateException, + SystemException; + + /** + * Obtain the status of the transaction associated with the target + * Transaction object. + * + * @return The transaction status. If no transaction is associated with + * the target object, this method returns the + * Status.NoTransaction value. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public int getStatus() throws SystemException; + + /** + * Register a synchronization object for the transaction currently + * associated with the target object. The transction manager invokes + * the beforeCompletion method prior to starting the two-phase transaction + * commit process. After the transaction is completed, the transaction + * manager invokes the afterCompletion method. + * + * @param sync The Synchronization object for the transaction associated + * with the target object. + * + * @exception RollbackException Thrown to indicate that + * the transaction has been marked for rollback only. + * + * @exception IllegalStateException Thrown if the transaction in the + * target object is in the prepared state or the transaction is + * inactive. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public void registerSynchronization(Synchronization sync) + throws RollbackException, IllegalStateException, + SystemException; + + /** + * Rollback the transaction represented by this Transaction object. + * + * @exception IllegalStateException Thrown if the transaction in the + * target object is in the prepared state or the transaction is + * inactive. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public void rollback() throws IllegalStateException, SystemException; + + /** + * Modify the transaction associated with the target object such that + * the only possible outcome of the transaction is to roll back the + * transaction. + * + * @exception IllegalStateException Thrown if the target object is + * not associated with any transaction. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public void setRollbackOnly() throws IllegalStateException, + SystemException; + +} diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionManager.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionManager.java new file mode 100644 index 000000000..6ac4e801d --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionManager.java @@ -0,0 +1,206 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +import java.lang.IllegalStateException; +import java.lang.SecurityException; + +/** + * The TransactionManager interface defines the methods that allow an + * application server to manage transaction boundaries. + */ +public interface TransactionManager { + + /** + * Create a new transaction and associate it with the current thread. + * + * @exception NotSupportedException Thrown if the thread is already + * associated with a transaction and the Transaction Manager + * implementation does not support nested transactions. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public void begin() throws NotSupportedException, SystemException; + + /** + * Complete the transaction associated with the current thread. When this + * method completes, the thread is no longer associated with a transaction. + * + * @exception RollbackException Thrown to indicate that + * the transaction has been rolled back rather than committed. + * + * @exception HeuristicMixedException Thrown to indicate that a heuristic + * decision was made and that some relevant updates have been committed + * while others have been rolled back. + * + * @exception HeuristicRollbackException Thrown to indicate that a + * heuristic decision was made and that all relevant updates have been + * rolled back. + * + * @exception SecurityException Thrown to indicate that the thread is + * not allowed to commit the transaction. + * + * @exception IllegalStateException Thrown if the current thread is + * not associated with a transaction. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public void commit() throws RollbackException, + HeuristicMixedException, HeuristicRollbackException, SecurityException, + IllegalStateException, SystemException; + + /** + * Obtain the status of the transaction associated with the current thread. + * + * @return The transaction status. If no transaction is associated with + * the current thread, this method returns the Status.NoTransaction + * value. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public int getStatus() throws SystemException; + + /** + * Get the transaction object that represents the transaction + * context of the calling thread. + * + * @return the Transaction object representing the + * transaction associated with the calling thread. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public Transaction getTransaction() throws SystemException; + + /** + * Resume the transaction context association of the calling thread + * with the transaction represented by the supplied Transaction object. + * When this method returns, the calling thread is associated with the + * transaction context specified. + * + * @param tobj The Transaction object that represents the + * transaction to be resumed. + * + * @exception InvalidTransactionException Thrown if the parameter + * transaction object contains an invalid transaction. + * + * @exception IllegalStateException Thrown if the thread is already + * associated with another transaction. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + */ + public void resume(Transaction tobj) + throws InvalidTransactionException, IllegalStateException, + SystemException; + + /** + * Roll back the transaction associated with the current thread. When this + * method completes, the thread is no longer associated with a + * transaction. + * + * @exception SecurityException Thrown to indicate that the thread is + * not allowed to roll back the transaction. + * + * @exception IllegalStateException Thrown if the current thread is + * not associated with a transaction. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public void rollback() throws IllegalStateException, SecurityException, + SystemException; + + /** + * Modify the transaction associated with the current thread such that + * the only possible outcome of the transaction is to roll back the + * transaction. + * + * @exception IllegalStateException Thrown if the current thread is + * not associated with a transaction. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public void setRollbackOnly() throws IllegalStateException, SystemException; + + /** + * Modify the timeout value that is associated with transactions started + * by the current thread with the begin method. + * + *

    If an application has not called this method, the transaction + * service uses some default value for the transaction timeout. + * + * @param seconds The value of the timeout in seconds. If the value is zero, + * the transaction service restores the default value. If the value + * is negative a SystemException is thrown. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public void setTransactionTimeout(int seconds) throws SystemException; + + /** + * Suspend the transaction currently associated with the calling + * thread and return a Transaction object that represents the + * transaction context being suspended. If the calling thread is + * not associated with a transaction, the method returns a null + * object reference. When this method returns, the calling thread + * is not associated with a transaction. + * + * @return Transaction object representing the suspended transaction. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + public Transaction suspend() throws SystemException; +} diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionRequiredException.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionRequiredException.java new file mode 100644 index 000000000..ac2b0f508 --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionRequiredException.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * This exception indicates that a request carried a null transaction context, + * but the target object requires an active transaction. + */ +public class TransactionRequiredException extends java.rmi.RemoteException +{ + + /** + * Specify serialVersionUID for backward compatibility + */ + private static final long serialVersionUID = -1898806419937446439L; + + public TransactionRequiredException() + { + super(); + } + + public TransactionRequiredException(String msg) + { + super(msg); + } +} + diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionRolledbackException.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionRolledbackException.java new file mode 100644 index 000000000..b79d03a15 --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionRolledbackException.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * This exception indicates that the transaction associated with processing + * of the request has been rolled back, or it has been marked to roll back. + * Thus the requested operation either could not be performed or was not + * performed because further computation on behalf of the transaction would be + * fruitless. + */ +public class TransactionRolledbackException extends java.rmi.RemoteException +{ + + /** + * Specify serialVersionUID for backward compatibility + */ + private static final long serialVersionUID = -3142798139623020577L; + + public TransactionRolledbackException() + { + super(); + } + + public TransactionRolledbackException(String msg) + { + super(msg); + } +} + diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionScoped.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionScoped.java new file mode 100644 index 000000000..6b9e3778f --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionScoped.java @@ -0,0 +1,90 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.enterprise.context.NormalScope; + +/** + *

    The javax.transaction.TransactionScoped annotation provides the ability to + * specify a standard CDI scope to define bean instances whose lifecycle is + * scoped to the currently active JTA transaction. This annotation has no effect + * on classes which have non-contextual references such those defined as managed + * beans by the Java EE specification.

    + * The transaction scope is active when the return from a call to + * UserTransaction.getStatus or + * TransactionManager.getStatus + * is one of the following states: + *
      + *
    • Status.STATUS_ACTIVE + *
    • Status.STATUS_MARKED_ROLLBACK + *
    • Status.STATUS_PREPARED + *
    • Status.STATUS_UNKNOWN + *
    • Status.STATUS_PREPARING + *
    • Status.STATUS_COMMITTING + *
    • Status.STATUS_ROLLING_BACK + *
    + *

    It is not intended that the term "active" as defined here in relation to the + * TransactionScoped annotation should also apply to its use in relation to + * transaction context, lifecycle, etc. mentioned elsewhere in this + * specification. The object with this annotation will be associated with the + * current active JTA transaction when the object is used. This association must + * be retained through any transaction suspend or resume calls as well as any + * Synchronization.beforeCompletion callbacks. Any + * Synchronization.afterCompletion methods will be invoked in an undefined + * context. The way in which the JTA transaction is begun and completed + * (for example via UserTransaction, Transactional interceptor, etc.) is of no consequence. + * The contextual references used across different JTA transactions are distinct. + * Refer to the CDI specification for more details on contextual references. + * A javax.enterprise.context.ContextNotActiveException + * will be thrown if an object with this annotation is used when the + * transaction context is not active.

    + * + * @since JTA1.2 + */ +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) +@NormalScope(passivating=true) +public @interface TransactionScoped { +} diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionSynchronizationRegistry.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionSynchronizationRegistry.java new file mode 100644 index 000000000..19cbcc81b --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionSynchronizationRegistry.java @@ -0,0 +1,208 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +/** + * This interface is intended for use by system level application server + * components such as persistence managers, resource adapters, as well as + * EJB and Web application components. This provides the ability to + * register synchronization objects with special ordering semantics, + * associate resource objects with the current transaction, get the + * transaction context of the current transaction, get current transaction + * status, and mark the current transaction for rollback. + * + * This interface is implemented by the application server by a + * stateless service object. The same object can be used by any number of + * components with thread safety. + * + *

    In standard application server environments, an instance + * implementing this interface can be looked up by a standard name via JNDI. + * The standard name is java:comp/TransactionSynchronizationRegistry. + * + * @since JTA 1.1 + */ +public interface TransactionSynchronizationRegistry { + + /** + * Return an opaque object to represent the transaction bound to the + * current thread at the time this method is called. This object + * overrides hashCode and equals to allow its use as the key in a + * hashMap for use by the caller. If there is no transaction currently + * active, return null. + * + *

    This object will return the same hashCode and compare equal to + * all other objects returned by calling this method + * from any component executing in the same transaction context in the + * same application server. + * + *

    The toString method returns a String that might be usable by a + * human reader to usefully understand the transaction context. The + * toString result is otherwise not defined. Specifically, there is no + * forward or backward compatibility guarantee of the results of + * toString. + * + *

    The object is not necessarily serializable, and has no defined + * behavior outside the virtual machine whence it was obtained. + * + * @return an opaque object representing the transaction bound to the + * current thread at the time this method is called. + * + * @since JTA 1.1 + */ + Object getTransactionKey(); + + /** + * Add or replace an object in the Map of resources being managed for + * the transaction bound to the current thread at the time this + * method is called. The supplied key should be of an caller- + * defined class so as not to conflict with other users. The class + * of the key must guarantee that the hashCode and equals methods are + * suitable for use as keys in a map. The key and value are not examined + * or used by the implementation. The general contract of this method + * is that of {@link java.util.Map#put(Object, Object)} for a Map that + * supports non-null keys and null values. For example, + * if there is already an value associated with the key, it is replaced + * by the value parameter. + * + * @param key the key for the Map entry. + * @param value the value for the Map entry. + * @exception IllegalStateException if no transaction is active. + * @exception NullPointerException if the parameter key is null. + * + * @since JTA 1.1 + */ + void putResource(Object key, Object value); + + /** + * Get an object from the Map of resources being managed for + * the transaction bound to the current thread at the time this + * method is called. The key should have been supplied earlier + * by a call to putResouce in the same transaction. If the key + * cannot be found in the current resource Map, null is returned. + * The general contract of this method + * is that of {@link java.util.Map#get(Object)} for a Map that + * supports non-null keys and null values. For example, + * the returned value is null if there is no entry for the parameter + * key or if the value associated with the key is actually null. + * + * @param key the key for the Map entry. + * @return the value associated with the key. + * @exception IllegalStateException if no transaction is active. + * @exception NullPointerException if the parameter key is null. + * + * @since JTA 1.1 + */ + Object getResource(Object key); + + /** + * Register a Synchronization instance with special ordering + * semantics. Its beforeCompletion will be called after all + * SessionSynchronization beforeCompletion callbacks and callbacks + * registered directly with the Transaction, but before the 2-phase + * commit process starts. Similarly, the afterCompletion + * callback will be called after 2-phase commit completes but before + * any SessionSynchronization and Transaction afterCompletion callbacks. + * + *

    The beforeCompletion callback will be invoked in the transaction + * context of the transaction bound to the current thread at the time + * this method is called. + * Allowable methods include access to resources, + * e.g. Connectors. No access is allowed to "user components" (e.g. timer + * services or bean methods), as these might change the state of data + * being managed by the caller, and might change the state of data that + * has already been flushed by another caller of + * registerInterposedSynchronization. + * The general context is the component + * context of the caller of registerInterposedSynchronization. + * + *

    The afterCompletion callback will be invoked in an undefined + * context. No access is permitted to "user components" + * as defined above. Resources can be closed but no transactional + * work can be performed with them. + * + *

    If this method is invoked without an active transaction context, an + * IllegalStateException is thrown. + * + *

    If this method is invoked after the two-phase commit processing has + * started, an IllegalStateException is thrown. + * + * @param sync the Synchronization instance. + * @exception IllegalStateException if no transaction is active. + * + * @since JTA 1.1 + */ + void registerInterposedSynchronization(Synchronization sync); + + /** + * Return the status of the transaction bound to the + * current thread at the time this method is called. + * This is the result of executing TransactionManager.getStatus() in + * the context of the transaction bound to the current thread at the time + * this method is called. + * + * @return the status of the transaction bound to the current thread + * at the time this method is called. + * + * @since JTA 1.1 + */ + int getTransactionStatus(); + + /** + * Set the rollbackOnly status of the transaction bound to the + * current thread at the time this method is called. + * + * @exception IllegalStateException if no transaction is active. + * + * @since JTA 1.1 + */ + void setRollbackOnly(); + + /** + * Get the rollbackOnly status of the transaction bound to the + * current thread at the time this method is called. + * + * @return the rollbackOnly status. + * @exception IllegalStateException if no transaction is active. + * + * @since JTA 1.1 + */ + boolean getRollbackOnly(); +} diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/Transactional.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/Transactional.java new file mode 100644 index 000000000..12bf9c8aa --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/Transactional.java @@ -0,0 +1,180 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package javax.transaction; + +import javax.enterprise.util.Nonbinding; +import javax.interceptor.InterceptorBinding; +import java.lang.annotation.*; + +/** + *

    The javax.transaction.Transactional annotation provides the application + * the ability to declaratively control transaction boundaries on CDI managed beans, as + * well as classes defined as managed beans by the Java EE specification, at both the class + * and method level where method level annotations override those at the class level.

    + *

    See the EJB specification for restrictions on the use of @Transactional with EJBs.

    + *

    This support is provided via an implementation of CDI interceptors that conduct the + * necessary suspending, resuming, etc. The Transactional interceptor interposes on business method + * invocations only and not on lifecycle events. Lifecycle methods are invoked in an unspecified + * transaction context.

    + *

    If an attempt is made to call any method of the UserTransaction interface from within the + * scope of a bean or method annotated with @Transactional and a Transactional.TxType other than + * NOT_SUPPORTED or NEVER, an IllegalStateException must be thrown. The use of the UserTransaction + * is allowed within life cycle events. The use of the TransactionSynchronizationRegistry is allowed + * regardless of any @Transactional annotation.

    + *

    The Transactional interceptors must have a priority of + * Interceptor.Priority.PLATFORM_BEFORE+200. + * Refer to the Interceptors specification for more details.

    + *

    The TxType element of the annotation indicates whether a bean method is to be executed within + * a transaction context. TxType.REQUIRED is the default.

    + * + *

    By default checked exceptions do not result in the transactional interceptor marking the + * transaction for rollback and instances of RuntimeException and its subclasses do. This default + * behavior can be modified by specifying exceptions that result in the interceptor marking the + * transaction for rollback and/or exceptions that do not result in rollback.

    + *

    The rollbackOn element can be set to indicate exceptions that must cause the interceptor to mark + * the transaction for rollback.

    + *

    Conversely, the dontRollbackOn element can be set to indicate + * exceptions that must not cause the interceptor to mark the transaction for rollback.

    + *

    When a class is specified for either of these elements, the designated behavior applies to subclasses + * of that class as well. If both elements are specified, dontRollbackOn takes precedence.

    + * + * @since JTA1.2 + */ +@Inherited +@InterceptorBinding +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface Transactional { + + /** + * The TxType element of the Transactional annotation indicates whether a bean method + * is to be executed within a transaction context. + */ + TxType value() default TxType.REQUIRED; + + /** + * The TxType element of the annotation indicates whether a bean method is to be + * executed within a transaction context where the values provide the following + * corresponding behavior. + */ + public enum TxType { + /** + *

    If called outside a transaction context, the interceptor must begin a new + * JTA transaction, the managed bean method execution must then continue + * inside this transaction context, and the transaction must be completed by + * the interceptor.

    + *

    If called inside a transaction context, the managed bean + * method execution must then continue inside this transaction context.

    + */ + REQUIRED, + + /** + *

    If called outside a transaction context, the interceptor must begin a new + * JTA transaction, the managed bean method execution must then continue + * inside this transaction context, and the transaction must be completed by + * the interceptor.

    + *

    If called inside a transaction context, the current transaction context must + * be suspended, a new JTA transaction will begin, the managed bean method + * execution must then continue inside this transaction context, the transaction + * must be completed, and the previously suspended transaction must be resumed.

    + */ + REQUIRES_NEW, + + /** + *

    If called outside a transaction context, a TransactionalException with a + * nested TransactionRequiredException must be thrown.

    + *

    If called inside a transaction context, managed bean method execution will + * then continue under that context.

    + */ + MANDATORY, + + /** + *

    If called outside a transaction context, managed bean method execution + * must then continue outside a transaction context.

    + *

    If called inside a transaction context, the managed bean method execution + * must then continue inside this transaction context.

    + */ + SUPPORTS, + + /** + *

    If called outside a transaction context, managed bean method execution + * must then continue outside a transaction context.

    + *

    If called inside a transaction context, the current transaction context must + * be suspended, the managed bean method execution must then continue + * outside a transaction context, and the previously suspended transaction + * must be resumed by the interceptor that suspended it after the method + * execution has completed.

    + */ + NOT_SUPPORTED, + + /** + *

    If called outside a transaction context, managed bean method execution + * must then continue outside a transaction context.

    + *

    If called inside a transaction context, a TransactionalException with + * a nested InvalidTransactionException must be thrown.

    + */ + NEVER + } + + /** + * The rollbackOn element can be set to indicate exceptions that must cause + * the interceptor to mark the transaction for rollback. Conversely, the dontRollbackOn + * element can be set to indicate exceptions that must not cause the interceptor to mark + * the transaction for rollback. When a class is specified for either of these elements, + * the designated behavior applies to subclasses of that class as well. If both elements + * are specified, dontRollbackOn takes precedence. + * @return Class[] of Exceptions + */ + @Nonbinding + public Class[] rollbackOn() default {}; + + /** + * The dontRollbackOn element can be set to indicate exceptions that must not cause + * the interceptor to mark the transaction for rollback. Conversely, the rollbackOn element + * can be set to indicate exceptions that must cause the interceptor to mark the transaction + * for rollback. When a class is specified for either of these elements, + * the designated behavior applies to subclasses of that class as well. If both elements + * are specified, dontRollbackOn takes precedence. + * @return Class[] of Exceptions + */ + @Nonbinding + public Class[] dontRollbackOn() default {}; + +} diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionalException.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionalException.java new file mode 100644 index 000000000..b064acc21 --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/TransactionalException.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package javax.transaction; + +/** + * + * The TransactionalException thrown from the Transactional interceptors + * implementation contains the original exception as its nested exception + * and is a RuntimeException, therefore, by default any + * transaction that was started as a result of a Transactional annotation + * earlier in the call stream will be marked for rollback as a result of + * the TransactionalException being thrown by the Transactional interceptor + * of the second bean. For example if a transaction is begun as a result of + * a call to a bean annotated with Transactional(TxType.REQUIRED) and this + * bean in turn calls a second bean annotated with + * Transactional(TxType.NEVER), the transaction begun by the first bean + * will be marked for rollback. + * + * @since JTA1.2 + */ +public class TransactionalException extends RuntimeException { + /** + * Specify serialVersionUID for backward compatibility + */ + private static final long serialVersionUID = -8196645329560986417L; + + public TransactionalException(String s, Throwable throwable) { + super(s, throwable); + } +} diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/UserTransaction.java b/fine-third-default/fine-javax-transaction/src/javax/transaction/UserTransaction.java new file mode 100644 index 000000000..2990bb06a --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/UserTransaction.java @@ -0,0 +1,154 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.transaction; + +import java.lang.IllegalArgumentException; +import java.lang.IllegalStateException; +import java.lang.SecurityException; + +/** + * The UserTransaction interface defines the methods that allow an + * application to explicitly manage transaction boundaries. + */ +public interface UserTransaction { + + /** + * Create a new transaction and associate it with the current thread. + * + * @exception NotSupportedException Thrown if the thread is already + * associated with a transaction and the Transaction Manager + * implementation does not support nested transactions. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + void begin() throws NotSupportedException, SystemException; + + /** + * Complete the transaction associated with the current thread. When this + * method completes, the thread is no longer associated with a transaction. + * + * @exception RollbackException Thrown to indicate that + * the transaction has been rolled back rather than committed. + * + * @exception HeuristicMixedException Thrown to indicate that a heuristic + * decision was made and that some relevant updates have been committed + * while others have been rolled back. + * + * @exception HeuristicRollbackException Thrown to indicate that a + * heuristic decision was made and that all relevant updates have been + * rolled back. + * + * @exception SecurityException Thrown to indicate that the thread is + * not allowed to commit the transaction. + * + * @exception IllegalStateException Thrown if the current thread is + * not associated with a transaction. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + */ + void commit() throws RollbackException, + HeuristicMixedException, HeuristicRollbackException, SecurityException, + IllegalStateException, SystemException; + + /** + * Roll back the transaction associated with the current thread. When this + * method completes, the thread is no longer associated with a transaction. + * + * @exception SecurityException Thrown to indicate that the thread is + * not allowed to roll back the transaction. + * + * @exception IllegalStateException Thrown if the current thread is + * not associated with a transaction. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + void rollback() throws IllegalStateException, SecurityException, + SystemException; + + /** + * Modify the transaction associated with the current thread such that + * the only possible outcome of the transaction is to roll back the + * transaction. + * + * @exception IllegalStateException Thrown if the current thread is + * not associated with a transaction. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + void setRollbackOnly() throws IllegalStateException, SystemException; + + /** + * Obtain the status of the transaction associated with the current thread. + * + * @return The transaction status. If no transaction is associated with + * the current thread, this method returns the Status.NoTransaction + * value. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + int getStatus() throws SystemException; + + /** + * Modify the timeout value that is associated with transactions started + * by the current thread with the begin method. + * + *

    If an application has not called this method, the transaction + * service uses some default value for the transaction timeout. + * + * @param seconds The value of the timeout in seconds. If the value is zero, + * the transaction service restores the default value. If the value + * is negative a SystemException is thrown. + * + * @exception SystemException Thrown if the transaction manager + * encounters an unexpected error condition. + * + */ + void setTransactionTimeout(int seconds) throws SystemException; +} diff --git a/fine-third-default/fine-javax-transaction/src/javax/transaction/package.html b/fine-third-default/fine-javax-transaction/src/javax/transaction/package.html new file mode 100644 index 000000000..877249d39 --- /dev/null +++ b/fine-third-default/fine-javax-transaction/src/javax/transaction/package.html @@ -0,0 +1,57 @@ + + + + + + + + +Provides the API that defines the contract between the transaction +manager and the various parties involved in a distributed transaction +namely : resource manager, application, and application server. +The implementation of this API is provided by the application +server vendor and the resource manager driver vendor. +

    + + + diff --git a/fine-third-default/fine-mail/src/com/sun/mail/auth/MD4.java b/fine-third-default/fine-mail/src/com/sun/mail/auth/MD4.java new file mode 100644 index 000000000..6979f108a --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/auth/MD4.java @@ -0,0 +1,300 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2005-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +/* + * Copied from OpenJDK with permission. + */ + +package com.sun.mail.auth; + +import java.security.*; + +//import static sun.security.provider.ByteArrayAccess.*; + +/** + * The MD4 class is used to compute an MD4 message digest over a given + * buffer of bytes. It is an implementation of the RSA Data Security Inc + * MD4 algorithim as described in internet RFC 1320. + * + * @author Andreas Sterbenz + * @author Bill Shannon (adapted for JavaMail) + */ +public final class MD4 { + + // state of this object + private final int[] state; + // temporary buffer, used by implCompress() + private final int[] x; + + // size of the input to the compression function in bytes + private static final int blockSize = 64; + + // buffer to store partial blocks, blockSize bytes large + private final byte[] buffer = new byte[blockSize]; + // offset into buffer + private int bufOfs; + + // number of bytes processed so far. + // also used as a flag to indicate reset status + // -1: need to call engineReset() before next call to update() + // 0: is already reset + private long bytesProcessed; + + // rotation constants + private static final int S11 = 3; + private static final int S12 = 7; + private static final int S13 = 11; + private static final int S14 = 19; + private static final int S21 = 3; + private static final int S22 = 5; + private static final int S23 = 9; + private static final int S24 = 13; + private static final int S31 = 3; + private static final int S32 = 9; + private static final int S33 = 11; + private static final int S34 = 15; + + private static final byte[] padding; + + static { + padding = new byte[136]; + padding[0] = (byte)0x80; + } + + /** + * Standard constructor, creates a new MD4 instance. + */ + public MD4() { + state = new int[4]; + x = new int[16]; + implReset(); + } + + /** + * Compute and return the message digest of the input byte array. + * + * @param in the input byte array + * @return the message digest byte array + */ + public byte[] digest(byte[] in) { + implReset(); + engineUpdate(in, 0, in.length); + byte[] out = new byte[16]; + implDigest(out, 0); + return out; + } + + /** + * Reset the state of this object. + */ + private void implReset() { + // Load magic initialization constants. + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + bufOfs = 0; + bytesProcessed = 0; + } + + /** + * Perform the final computations, any buffered bytes are added + * to the digest, the count is added to the digest, and the resulting + * digest is stored. + */ + private void implDigest(byte[] out, int ofs) { + long bitsProcessed = bytesProcessed << 3; + + int index = (int)bytesProcessed & 0x3f; + int padLen = (index < 56) ? (56 - index) : (120 - index); + engineUpdate(padding, 0, padLen); + + //i2bLittle4((int)bitsProcessed, buffer, 56); + //i2bLittle4((int)(bitsProcessed >>> 32), buffer, 60); + buffer[56] = (byte)bitsProcessed; + buffer[57] = (byte)(bitsProcessed>>8); + buffer[58] = (byte)(bitsProcessed>>16); + buffer[59] = (byte)(bitsProcessed>>24); + buffer[60] = (byte)(bitsProcessed>>32); + buffer[61] = (byte)(bitsProcessed>>40); + buffer[62] = (byte)(bitsProcessed>>48); + buffer[63] = (byte)(bitsProcessed>>56); + implCompress(buffer, 0); + + //i2bLittle(state, 0, out, ofs, 16); + for (int i = 0; i < state.length; i++) { + int x = state[i]; + out[ofs++] = (byte)x; + out[ofs++] = (byte)(x>>8); + out[ofs++] = (byte)(x>>16); + out[ofs++] = (byte)(x>>24); + } + } + + private void engineUpdate(byte[] b, int ofs, int len) { + if (len == 0) { + return; + } + if ((ofs < 0) || (len < 0) || (ofs > b.length - len)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (bytesProcessed < 0) { + implReset(); + } + bytesProcessed += len; + // if buffer is not empty, we need to fill it before proceeding + if (bufOfs != 0) { + int n = Math.min(len, blockSize - bufOfs); + System.arraycopy(b, ofs, buffer, bufOfs, n); + bufOfs += n; + ofs += n; + len -= n; + if (bufOfs >= blockSize) { + // compress completed block now + implCompress(buffer, 0); + bufOfs = 0; + } + } + // compress complete blocks + while (len >= blockSize) { + implCompress(b, ofs); + len -= blockSize; + ofs += blockSize; + } + // copy remainder to buffer + if (len > 0) { + System.arraycopy(b, ofs, buffer, 0, len); + bufOfs = len; + } + } + + private static int FF(int a, int b, int c, int d, int x, int s) { + a += ((b & c) | ((~b) & d)) + x; + return ((a << s) | (a >>> (32 - s))); + } + + private static int GG(int a, int b, int c, int d, int x, int s) { + a += ((b & c) | (b & d) | (c & d)) + x + 0x5a827999; + return ((a << s) | (a >>> (32 - s))); + } + + private static int HH(int a, int b, int c, int d, int x, int s) { + a += ((b ^ c) ^ d) + x + 0x6ed9eba1; + return ((a << s) | (a >>> (32 - s))); + } + + /** + * This is where the functions come together as the generic MD4 + * transformation operation. It consumes 64 + * bytes from the buffer, beginning at the specified offset. + */ + private void implCompress(byte[] buf, int ofs) { + //b2iLittle64(buf, ofs, x); + for (int xfs = 0; xfs < x.length; xfs++) { + x[xfs] = (buf[ofs] & 0xff) | ((buf[ofs+1] & 0xff) << 8) | + ((buf[ofs+2] & 0xff) << 16) | ((buf[ofs+3] & 0xff) << 24); + ofs += 4; + } + + int a = state[0]; + int b = state[1]; + int c = state[2]; + int d = state[3]; + + /* Round 1 */ + a = FF (a, b, c, d, x[ 0], S11); /* 1 */ + d = FF (d, a, b, c, x[ 1], S12); /* 2 */ + c = FF (c, d, a, b, x[ 2], S13); /* 3 */ + b = FF (b, c, d, a, x[ 3], S14); /* 4 */ + a = FF (a, b, c, d, x[ 4], S11); /* 5 */ + d = FF (d, a, b, c, x[ 5], S12); /* 6 */ + c = FF (c, d, a, b, x[ 6], S13); /* 7 */ + b = FF (b, c, d, a, x[ 7], S14); /* 8 */ + a = FF (a, b, c, d, x[ 8], S11); /* 9 */ + d = FF (d, a, b, c, x[ 9], S12); /* 10 */ + c = FF (c, d, a, b, x[10], S13); /* 11 */ + b = FF (b, c, d, a, x[11], S14); /* 12 */ + a = FF (a, b, c, d, x[12], S11); /* 13 */ + d = FF (d, a, b, c, x[13], S12); /* 14 */ + c = FF (c, d, a, b, x[14], S13); /* 15 */ + b = FF (b, c, d, a, x[15], S14); /* 16 */ + + /* Round 2 */ + a = GG (a, b, c, d, x[ 0], S21); /* 17 */ + d = GG (d, a, b, c, x[ 4], S22); /* 18 */ + c = GG (c, d, a, b, x[ 8], S23); /* 19 */ + b = GG (b, c, d, a, x[12], S24); /* 20 */ + a = GG (a, b, c, d, x[ 1], S21); /* 21 */ + d = GG (d, a, b, c, x[ 5], S22); /* 22 */ + c = GG (c, d, a, b, x[ 9], S23); /* 23 */ + b = GG (b, c, d, a, x[13], S24); /* 24 */ + a = GG (a, b, c, d, x[ 2], S21); /* 25 */ + d = GG (d, a, b, c, x[ 6], S22); /* 26 */ + c = GG (c, d, a, b, x[10], S23); /* 27 */ + b = GG (b, c, d, a, x[14], S24); /* 28 */ + a = GG (a, b, c, d, x[ 3], S21); /* 29 */ + d = GG (d, a, b, c, x[ 7], S22); /* 30 */ + c = GG (c, d, a, b, x[11], S23); /* 31 */ + b = GG (b, c, d, a, x[15], S24); /* 32 */ + + /* Round 3 */ + a = HH (a, b, c, d, x[ 0], S31); /* 33 */ + d = HH (d, a, b, c, x[ 8], S32); /* 34 */ + c = HH (c, d, a, b, x[ 4], S33); /* 35 */ + b = HH (b, c, d, a, x[12], S34); /* 36 */ + a = HH (a, b, c, d, x[ 2], S31); /* 37 */ + d = HH (d, a, b, c, x[10], S32); /* 38 */ + c = HH (c, d, a, b, x[ 6], S33); /* 39 */ + b = HH (b, c, d, a, x[14], S34); /* 40 */ + a = HH (a, b, c, d, x[ 1], S31); /* 41 */ + d = HH (d, a, b, c, x[ 9], S32); /* 42 */ + c = HH (c, d, a, b, x[ 5], S33); /* 43 */ + b = HH (b, c, d, a, x[13], S34); /* 44 */ + a = HH (a, b, c, d, x[ 3], S31); /* 45 */ + d = HH (d, a, b, c, x[11], S32); /* 46 */ + c = HH (c, d, a, b, x[ 7], S33); /* 47 */ + b = HH (b, c, d, a, x[15], S34); /* 48 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/auth/Ntlm.java b/fine-third-default/fine-mail/src/com/sun/mail/auth/Ntlm.java new file mode 100644 index 000000000..561f432a6 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/auth/Ntlm.java @@ -0,0 +1,367 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2005-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +/* + * Copied from OpenJDK with permission. + */ + +package com.sun.mail.auth; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.io.PrintStream; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.util.Locale; +import java.util.logging.Level; +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; + +import com.sun.mail.util.BASE64DecoderStream; +import com.sun.mail.util.BASE64EncoderStream; +import com.sun.mail.util.MailLogger; + + +/** + * NTLMAuthentication: + * + * @author Michael McMahon + * @author Bill Shannon (adapted for JavaMail) + */ +public class Ntlm { + + private byte[] type1; + private byte[] type3; + + private SecretKeyFactory fac; + private Cipher cipher; + private MD4 md4; + private String hostname; + private String ntdomain; + private String username; + private String password; + + private MailLogger logger; + + private void init0() { + type1 = new byte[256]; + type3 = new byte[256]; + System.arraycopy(new byte[] {'N','T','L','M','S','S','P',0,1}, 0, + type1, 0, 9); + type1[12] = (byte) 3; + type1[13] = (byte) 0xb2; + type1[28] = (byte) 0x20; + System.arraycopy(new byte[] {'N','T','L','M','S','S','P',0,3}, 0, + type3, 0, 9); + type3[12] = (byte) 0x18; + type3[14] = (byte) 0x18; + type3[20] = (byte) 0x18; + type3[22] = (byte) 0x18; + type3[32] = (byte) 0x40; + type3[60] = (byte) 1; + type3[61] = (byte) 0x82; + + try { + fac = SecretKeyFactory.getInstance("DES"); + cipher = Cipher.getInstance("DES/ECB/NoPadding"); + md4 = new MD4(); + } catch (NoSuchPaddingException e) { + assert false; + } catch (NoSuchAlgorithmException e) { + assert false; + } + }; + + /** + * Create an NTLM authenticator. + * Username may be specified as domain\\username in the Authenticator. + * If this notation is not used, then the domain will be taken + * from the ntdomain parameter. + * + * @param ntdomain the NT domain + * @param hostname the host name + * @param username the user name + * @param password the password + * @param logger the MailLogger + */ + public Ntlm(String ntdomain, String hostname, String username, + String password, MailLogger logger) { + int i = hostname.indexOf('.'); + if (i != -1) { + hostname = hostname.substring(0, i); + } + i = username.indexOf('\\'); + if (i != -1) { + ntdomain = username.substring(0, i).toUpperCase(Locale.ENGLISH); + username = username.substring(i+1); + } else if (ntdomain == null) { + ntdomain = ""; + } + this.ntdomain = ntdomain; + this.hostname = hostname; + this.username = username; + this.password = password; + this.logger = logger.getLogger(this.getClass(), "DEBUG NTLM"); + init0(); + } + + private void copybytes(byte[] dest, int destpos, String src, String enc) { + try { + byte[] x = src.getBytes(enc); + System.arraycopy(x, 0, dest, destpos, x.length); + } catch (UnsupportedEncodingException e) { + assert false; + } + } + + public String generateType1Msg(int flags) { + // XXX - should set "flags" in generated message + int dlen = ntdomain.length(); + type1[16]= (byte) (dlen % 256); + type1[17]= (byte) (dlen / 256); + type1[18] = type1[16]; + type1[19] = type1[17]; + if (dlen == 0) + type1[13] &= ~0x10; + + int hlen = hostname.length(); + type1[24]= (byte) (hlen % 256); + type1[25]= (byte) (hlen / 256); + type1[26] = type1[24]; + type1[27] = type1[25]; + + copybytes(type1, 32, hostname, "iso-8859-1"); + copybytes(type1, hlen+32, ntdomain, "iso-8859-1"); + type1[20] = (byte) ((hlen+32) % 256); + type1[21] = (byte) ((hlen+32) / 256); + + byte[] msg = new byte[32 + hlen + dlen]; + System.arraycopy(type1, 0, msg, 0, 32 + hlen + dlen); + if (logger.isLoggable(Level.FINE)) + logger.fine("type 1 message: " + toHex(msg)); + + String result = null; + try { + result = new String(BASE64EncoderStream.encode(msg), "iso-8859-1"); + } catch (UnsupportedEncodingException e) { + assert false; + } + return result; + } + + /** + * Convert a 7 byte array to an 8 byte array (for a des key with parity). + * Input starts at offset off. + */ + private byte[] makeDesKey(byte[] input, int off) { + int[] in = new int[input.length]; + for (int i = 0; i < in.length; i++) { + in[i] = input[i] < 0 ? input[i] + 256: input[i]; + } + byte[] out = new byte[8]; + out[0] = (byte)in[off+0]; + out[1] = (byte)(((in[off+0] << 7) & 0xFF) | (in[off+1] >> 1)); + out[2] = (byte)(((in[off+1] << 6) & 0xFF) | (in[off+2] >> 2)); + out[3] = (byte)(((in[off+2] << 5) & 0xFF) | (in[off+3] >> 3)); + out[4] = (byte)(((in[off+3] << 4) & 0xFF) | (in[off+4] >> 4)); + out[5] = (byte)(((in[off+4] << 3) & 0xFF) | (in[off+5] >> 5)); + out[6] = (byte)(((in[off+5] << 2) & 0xFF) | (in[off+6] >> 6)); + out[7] = (byte)((in[off+6] << 1) & 0xFF); + return out; + } + + private byte[] calcLMHash() throws GeneralSecurityException { + byte[] magic = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + byte[] pwb = null; + try { + pwb = password.toUpperCase(Locale.ENGLISH).getBytes("iso-8859-1"); + } catch (UnsupportedEncodingException ex) { + // should never happen + assert false; + } + byte[] pwb1 = new byte[14]; + int len = password.length(); + if (len > 14) + len = 14; + System.arraycopy(pwb, 0, pwb1, 0, len); /* Zero padded */ + + DESKeySpec dks1 = new DESKeySpec(makeDesKey(pwb1, 0)); + DESKeySpec dks2 = new DESKeySpec(makeDesKey(pwb1, 7)); + + SecretKey key1 = fac.generateSecret(dks1); + SecretKey key2 = fac.generateSecret(dks2); + cipher.init(Cipher.ENCRYPT_MODE, key1); + byte[] out1 = cipher.doFinal(magic, 0, 8); + cipher.init(Cipher.ENCRYPT_MODE, key2); + byte[] out2 = cipher.doFinal(magic, 0, 8); + + byte[] result = new byte [21]; + System.arraycopy(out1, 0, result, 0, 8); + System.arraycopy(out2, 0, result, 8, 8); + return result; + } + + private byte[] calcNTHash() throws GeneralSecurityException { + byte[] pw = null; + try { + pw = password.getBytes("UnicodeLittleUnmarked"); + } catch (UnsupportedEncodingException e) { + assert false; + } + byte[] out = md4.digest(pw); + byte[] result = new byte[21]; + System.arraycopy(out, 0, result, 0, 16); + return result; + } + + /* + * Key is a 21 byte array. Split it into 3 7 byte chunks, + * convert each to 8 byte DES keys, encrypt the text arg with + * each key and return the three results in a sequential []. + */ + private byte[] calcResponse(byte[] key, byte[] text) + throws GeneralSecurityException { + assert key.length == 21; + DESKeySpec dks1 = new DESKeySpec(makeDesKey(key, 0)); + DESKeySpec dks2 = new DESKeySpec(makeDesKey(key, 7)); + DESKeySpec dks3 = new DESKeySpec(makeDesKey(key, 14)); + SecretKey key1 = fac.generateSecret(dks1); + SecretKey key2 = fac.generateSecret(dks2); + SecretKey key3 = fac.generateSecret(dks3); + cipher.init(Cipher.ENCRYPT_MODE, key1); + byte[] out1 = cipher.doFinal(text, 0, 8); + cipher.init(Cipher.ENCRYPT_MODE, key2); + byte[] out2 = cipher.doFinal(text, 0, 8); + cipher.init(Cipher.ENCRYPT_MODE, key3); + byte[] out3 = cipher.doFinal(text, 0, 8); + byte[] result = new byte[24]; + System.arraycopy(out1, 0, result, 0, 8); + System.arraycopy(out2, 0, result, 8, 8); + System.arraycopy(out3, 0, result, 16, 8); + return result; + } + + public String generateType3Msg(String challenge) { + try { + /* First decode the type2 message to get the server nonce */ + /* nonce is located at type2[24] for 8 bytes */ + + byte[] type2 = null; + try { + type2 = BASE64DecoderStream.decode(challenge.getBytes("us-ascii")); + } catch (UnsupportedEncodingException ex) { + // should never happen + assert false; + } + byte[] nonce = new byte[8]; + System.arraycopy(type2, 24, nonce, 0, 8); + + int ulen = username.length()*2; + type3[36] = type3[38] = (byte) (ulen % 256); + type3[37] = type3[39] = (byte) (ulen / 256); + int dlen = ntdomain.length()*2; + type3[28] = type3[30] = (byte) (dlen % 256); + type3[29] = type3[31] = (byte) (dlen / 256); + int hlen = hostname.length()*2; + type3[44] = type3[46] = (byte) (hlen % 256); + type3[45] = type3[47] = (byte) (hlen / 256); + + int l = 64; + copybytes(type3, l, ntdomain, "UnicodeLittleUnmarked"); + type3[32] = (byte) (l % 256); + type3[33] = (byte) (l / 256); + l += dlen; + copybytes(type3, l, username, "UnicodeLittleUnmarked"); + type3[40] = (byte) (l % 256); + type3[41] = (byte) (l / 256); + l += ulen; + copybytes(type3, l, hostname, "UnicodeLittleUnmarked"); + type3[48] = (byte) (l % 256); + type3[49] = (byte) (l / 256); + l += hlen; + + byte[] lmhash = calcLMHash(); + byte[] lmresponse = calcResponse(lmhash, nonce); + byte[] nthash = calcNTHash(); + byte[] ntresponse = calcResponse(nthash, nonce); + System.arraycopy(lmresponse, 0, type3, l, 24); + type3[16] = (byte) (l % 256); + type3[17] = (byte) (l / 256); + l += 24; + System.arraycopy(ntresponse, 0, type3, l, 24); + type3[24] = (byte) (l % 256); + type3[25] = (byte) (l / 256); + l += 24; + type3[56] = (byte) (l % 256); + type3[57] = (byte) (l / 256); + + byte[] msg = new byte[l]; + System.arraycopy(type3, 0, msg, 0, l); + if (logger.isLoggable(Level.FINE)) + logger.fine("type 3 message: " + toHex(msg)); + + String result = null; + try { + result = new String(BASE64EncoderStream.encode(msg), "iso-8859-1"); + } catch (UnsupportedEncodingException e) { + assert false; + } + return result; + + } catch (GeneralSecurityException ex) { + // should never happen + logger.log(Level.FINE, "GeneralSecurityException", ex); + return ""; // will fail later + } + } + + private static char[] hex = + { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; + + private static String toHex(byte[] b) { + StringBuilder sb = new StringBuilder(b.length * 3); + for (int i = 0; i < b.length; i++) + sb.append(hex[(b[i]>>4)&0xF]).append(hex[b[i]&0xF]).append(' '); + return sb.toString(); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/auth/OAuth2SaslClient.java b/fine-third-default/fine-mail/src/com/sun/mail/auth/OAuth2SaslClient.java new file mode 100644 index 000000000..42ab7c594 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/auth/OAuth2SaslClient.java @@ -0,0 +1,144 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2014-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.auth; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Map; +import java.security.Provider; +import java.security.Security; +import javax.security.sasl.*; +import javax.security.auth.callback.*; + +import com.sun.mail.util.ASCIIUtility; + +/** + * JavaMail SASL client for OAUTH2. + * + * @see + * RFC 6749 - OAuth 2.0 Authorization Framework + * @see + * RFC 6750 - OAuth 2.0 Authorization Framework: Bearer Token Usage + * @author Bill Shannon + */ +public class OAuth2SaslClient implements SaslClient { + private CallbackHandler cbh; + //private Map props; // XXX - not currently used + private boolean complete = false; + + public OAuth2SaslClient(Map props, CallbackHandler cbh) { + //this.props = props; + this.cbh = cbh; + } + + @Override + public String getMechanismName() { + return "XOAUTH2"; + } + + @Override + public boolean hasInitialResponse() { + return true; + } + + @Override + public byte[] evaluateChallenge(byte[] challenge) throws SaslException { + if (complete) + return new byte[0]; + + NameCallback ncb = new NameCallback("User name:"); + PasswordCallback pcb = new PasswordCallback("OAuth token:", false); + try { + cbh.handle(new Callback[] { ncb, pcb }); + } catch (UnsupportedCallbackException ex) { + throw new SaslException("Unsupported callback", ex); + } catch (IOException ex) { + throw new SaslException("Callback handler failed", ex); + } + + /* + * The OAuth token isn't really a password, and JavaMail doesn't + * use char[] for passwords, so we don't worry about storing the + * token in strings. + */ + String user = ncb.getName(); + String token = new String(pcb.getPassword()); + pcb.clearPassword(); + String resp = "user=" + user + "\001auth=Bearer " + token + "\001\001"; + byte[] response; + try { + response = resp.getBytes("utf-8"); + } catch (UnsupportedEncodingException ex) { + // fall back to ASCII + response = ASCIIUtility.getBytes(resp); + } + complete = true; + return response; + } + + @Override + public boolean isComplete() { + return complete; + } + + @Override + public byte[] unwrap(byte[] incoming, int offset, int len) + throws SaslException { + throw new IllegalStateException("OAUTH2 unwrap not supported"); + } + + @Override + public byte[] wrap(byte[] outgoing, int offset, int len) + throws SaslException { + throw new IllegalStateException("OAUTH2 wrap not supported"); + } + + @Override + public Object getNegotiatedProperty(String propName) { + if (!complete) + throw new IllegalStateException("OAUTH2 getNegotiatedProperty"); + return null; + } + + @Override + public void dispose() throws SaslException { + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/auth/OAuth2SaslClientFactory.java b/fine-third-default/fine-mail/src/com/sun/mail/auth/OAuth2SaslClientFactory.java new file mode 100644 index 000000000..21fc208ee --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/auth/OAuth2SaslClientFactory.java @@ -0,0 +1,97 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2014-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.auth; + +import java.util.*; +import java.security.Provider; +import java.security.Security; +import javax.security.sasl.*; +import javax.security.auth.callback.*; + +/** + * JavaMail SASL client factory for OAUTH2. + * + * @author Bill Shannon + */ +public class OAuth2SaslClientFactory implements SaslClientFactory { + + private static final String PROVIDER_NAME = "JavaMail-OAuth2"; + private static final String MECHANISM_NAME = "SaslClientFactory.XOAUTH2"; + + static class OAuth2Provider extends Provider { + private static final long serialVersionUID = -5371795551562287059L; + + public OAuth2Provider() { + super(PROVIDER_NAME, 1.0, "XOAUTH2 SASL Mechanism"); + put(MECHANISM_NAME, OAuth2SaslClientFactory.class.getName()); + } + } + + @Override + public SaslClient createSaslClient(String[] mechanisms, + String authorizationId, String protocol, + String serverName, Map props, + CallbackHandler cbh) throws SaslException { + for (String m : mechanisms) { + if (m.equals("XOAUTH2")) + return new OAuth2SaslClient(props, cbh); + } + return null; + } + + @Override + public String[] getMechanismNames(Map props) { + return new String[] { "XOAUTH2" }; + } + + /** + * Initialize this OAUTH2 provider, but only if there isn't one already. + * If we're not allowed to add this provider, just give up silently. + */ + public static void init() { + try { + if (Security.getProvider(PROVIDER_NAME) == null) + Security.addProvider(new OAuth2Provider()); + } catch (SecurityException ex) { + // oh well... + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/auth/package.html b/fine-third-default/fine-mail/src/com/sun/mail/auth/package.html new file mode 100644 index 000000000..727616516 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/auth/package.html @@ -0,0 +1,57 @@ + + + + + + +com.sun.mail.auth package + + + +

    +This package includes internal authentication support classes and +SHOULD NOT BE USED DIRECTLY BY APPLICATIONS. +

    + + + diff --git a/fine-third-default/fine-mail/src/com/sun/mail/handlers/handler_base.java b/fine-third-default/fine-mail/src/com/sun/mail/handlers/handler_base.java new file mode 100644 index 000000000..31fb4f42a --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/handlers/handler_base.java @@ -0,0 +1,109 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.handlers; + +import java.io.IOException; +import java.awt.datatransfer.DataFlavor; +import javax.activation.*; + +/** + * Base class for other DataContentHandlers. + */ +public abstract class handler_base implements DataContentHandler { + + /** + * Return an array of ActivationDataFlavors that we support. + * Usually there will be only one. + * + * @return array of ActivationDataFlavors that we support + */ + protected abstract ActivationDataFlavor[] getDataFlavors(); + + /** + * Given the flavor that matched, return the appropriate type of object. + * Usually there's only one flavor so just call getContent. + * + * @param aFlavor the ActivationDataFlavor + * @param ds DataSource containing the data + * @return the object + * @exception IOException for errors reading the data + */ + protected Object getData(ActivationDataFlavor aFlavor, DataSource ds) + throws IOException { + return getContent(ds); + } + + /** + * Return the DataFlavors for this DataContentHandler. + * + * @return The DataFlavors + */ + @Override + public DataFlavor[] getTransferDataFlavors() { + ActivationDataFlavor[] adf = getDataFlavors(); + if (adf.length == 1) // the common case + return new DataFlavor[] { adf[0] }; + DataFlavor[] df = new DataFlavor[adf.length]; + System.arraycopy(adf, 0, df, 0, adf.length); + return df; + } + + /** + * Return the Transfer Data of type DataFlavor from InputStream. + * + * @param df The DataFlavor + * @param ds The DataSource corresponding to the data + * @return the object + * @exception IOException for errors reading the data + */ + @Override + public Object getTransferData(DataFlavor df, DataSource ds) + throws IOException { + ActivationDataFlavor[] adf = getDataFlavors(); + for (int i = 0; i < adf.length; i++) { + // use ActivationDataFlavor.equals, which properly + // ignores Content-Type parameters in comparison + if (adf[i].equals(df)) + return getData(adf[i], ds); + } + return null; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/handlers/image_gif.java b/fine-third-default/fine-mail/src/com/sun/mail/handlers/image_gif.java new file mode 100644 index 000000000..95d3a440b --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/handlers/image_gif.java @@ -0,0 +1,98 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.handlers; + +import java.io.*; +import java.awt.*; +import javax.activation.*; + +/** + * DataContentHandler for image/gif. + */ +public class image_gif extends handler_base { + private static ActivationDataFlavor[] myDF = { + new ActivationDataFlavor(Image.class, "image/gif", "GIF Image") + }; + + @Override + protected ActivationDataFlavor[] getDataFlavors() { + return myDF; + } + + @Override + public Object getContent(DataSource ds) throws IOException { + InputStream is = ds.getInputStream(); + int pos = 0; + int count; + byte buf[] = new byte[1024]; + + while ((count = is.read(buf, pos, buf.length - pos)) != -1) { + pos += count; + if (pos >= buf.length) { + int size = buf.length; + if (size < 256*1024) + size += size; + else + size += 256*1024; + byte tbuf[] = new byte[size]; + System.arraycopy(buf, 0, tbuf, 0, pos); + buf = tbuf; + } + } + Toolkit tk = Toolkit.getDefaultToolkit(); + return tk.createImage(buf, 0, pos); + } + + /** + * Write the object to the output stream, using the specified MIME type. + */ + @Override + public void writeTo(Object obj, String type, OutputStream os) + throws IOException { + if (!(obj instanceof Image)) + throw new IOException("\"" + getDataFlavors()[0].getMimeType() + + "\" DataContentHandler requires Image object, " + + "was given object of type " + obj.getClass().toString()); + + throw new IOException(getDataFlavors()[0].getMimeType() + + " encoding not supported"); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/handlers/image_jpeg.java b/fine-third-default/fine-mail/src/com/sun/mail/handlers/image_jpeg.java new file mode 100644 index 000000000..ef663290e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/handlers/image_jpeg.java @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.handlers; + +import java.awt.Image; +import javax.activation.ActivationDataFlavor; + +/** + * DataContentHandler for image/jpeg. + */ +public class image_jpeg extends image_gif { + private static ActivationDataFlavor[] myDF = { + new ActivationDataFlavor(Image.class, "image/jpeg", "JPEG Image") + }; + + @Override + protected ActivationDataFlavor[] getDataFlavors() { + return myDF; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/handlers/message_rfc822.java b/fine-third-default/fine-mail/src/com/sun/mail/handlers/message_rfc822.java new file mode 100644 index 000000000..b488abc56 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/handlers/message_rfc822.java @@ -0,0 +1,114 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.handlers; + +import java.io.*; +import java.util.Properties; +import javax.activation.*; +import javax.mail.*; +import javax.mail.internet.*; + + +/** + * @author Christopher Cotton + */ + + +public class message_rfc822 extends handler_base { + + private static ActivationDataFlavor[] ourDataFlavor = { + new ActivationDataFlavor(Message.class, "message/rfc822", "Message") + }; + + @Override + protected ActivationDataFlavor[] getDataFlavors() { + return ourDataFlavor; + } + + /** + * Return the content. + */ + @Override + public Object getContent(DataSource ds) throws IOException { + // create a new MimeMessage + try { + Session session; + if (ds instanceof MessageAware) { + MessageContext mc = ((MessageAware)ds).getMessageContext(); + session = mc.getSession(); + } else { + // Hopefully a rare case. Also hopefully the application + // has created a default Session that can just be returned + // here. If not, the one we create here is better than + // nothing, but overall not a really good answer. + session = Session.getDefaultInstance(new Properties(), null); + } + return new MimeMessage(session, ds.getInputStream()); + } catch (MessagingException me) { + IOException ioex = + new IOException("Exception creating MimeMessage in " + + "message/rfc822 DataContentHandler"); + ioex.initCause(me); + throw ioex; + } + } + + /** + * Write the object as a byte stream. + */ + @Override + public void writeTo(Object obj, String mimeType, OutputStream os) + throws IOException { + // if the object is a message, we know how to write that out + if (obj instanceof Message) { + Message m = (Message)obj; + try { + m.writeTo(os); + } catch (MessagingException me) { + IOException ioex = new IOException("Exception writing message"); + ioex.initCause(me); + throw ioex; + } + } else { + throw new IOException("unsupported object"); + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/handlers/multipart_mixed.java b/fine-third-default/fine-mail/src/com/sun/mail/handlers/multipart_mixed.java new file mode 100644 index 000000000..f5af1e057 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/handlers/multipart_mixed.java @@ -0,0 +1,93 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.handlers; + +import java.io.*; +import javax.activation.*; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.internet.MimeMultipart; + + +public class multipart_mixed extends handler_base { + private static ActivationDataFlavor[] myDF = { + new ActivationDataFlavor(Multipart.class, + "multipart/mixed", "Multipart") + }; + + @Override + protected ActivationDataFlavor[] getDataFlavors() { + return myDF; + } + + /** + * Return the content. + */ + @Override + public Object getContent(DataSource ds) throws IOException { + try { + return new MimeMultipart(ds); + } catch (MessagingException e) { + IOException ioex = + new IOException("Exception while constructing MimeMultipart"); + ioex.initCause(e); + throw ioex; + } + } + + /** + * Write the object to the output stream, using the specific MIME type. + */ + @Override + public void writeTo(Object obj, String mimeType, OutputStream os) + throws IOException { + if (obj instanceof Multipart) { + try { + ((Multipart)obj).writeTo(os); + } catch (MessagingException e) { + IOException ioex = + new IOException("Exception writing Multipart"); + ioex.initCause(e); + throw ioex; + } + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/handlers/package.html b/fine-third-default/fine-mail/src/com/sun/mail/handlers/package.html new file mode 100644 index 000000000..d2759d2a8 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/handlers/package.html @@ -0,0 +1,57 @@ + + + + + + +com.sun.mail.handlers package + + + +

    +This package includes internal data handler support classes and +SHOULD NOT BE USED DIRECTLY BY APPLICATIONS. +

    + + + diff --git a/fine-third-default/fine-mail/src/com/sun/mail/handlers/text_html.java b/fine-third-default/fine-mail/src/com/sun/mail/handlers/text_html.java new file mode 100644 index 000000000..dd14a07d0 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/handlers/text_html.java @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.handlers; + +import javax.activation.ActivationDataFlavor; + +/** + * DataContentHandler for text/html. + * + */ +public class text_html extends text_plain { + private static ActivationDataFlavor[] myDF = { + new ActivationDataFlavor(String.class, "text/html", "HTML String") + }; + + @Override + protected ActivationDataFlavor[] getDataFlavors() { + return myDF; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/handlers/text_plain.java b/fine-third-default/fine-mail/src/com/sun/mail/handlers/text_plain.java new file mode 100644 index 000000000..93f027521 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/handlers/text_plain.java @@ -0,0 +1,177 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.handlers; + +import java.io.*; +import javax.activation.*; +import javax.mail.internet.ContentType; +import javax.mail.internet.MimeUtility; + +/** + * DataContentHandler for text/plain. + * + */ +public class text_plain extends handler_base { + private static ActivationDataFlavor[] myDF = { + new ActivationDataFlavor(String.class, "text/plain", "Text String") + }; + + /** + * An OuputStream wrapper that doesn't close the underlying stream. + */ + private static class NoCloseOutputStream extends FilterOutputStream { + public NoCloseOutputStream(OutputStream os) { + super(os); + } + + @Override + public void close() { + // do nothing + } + } + + @Override + protected ActivationDataFlavor[] getDataFlavors() { + return myDF; + } + + @Override + public Object getContent(DataSource ds) throws IOException { + String enc = null; + InputStreamReader is = null; + + try { + enc = getCharset(ds.getContentType()); + is = new InputStreamReader(ds.getInputStream(), enc); + } catch (IllegalArgumentException iex) { + /* + * An unknown charset of the form ISO-XXX-XXX will cause + * the JDK to throw an IllegalArgumentException. The + * JDK will attempt to create a classname using this string, + * but valid classnames must not contain the character '-', + * and this results in an IllegalArgumentException, rather than + * the expected UnsupportedEncodingException. Yikes. + */ + throw new UnsupportedEncodingException(enc); + } + + try { + int pos = 0; + int count; + char buf[] = new char[1024]; + + while ((count = is.read(buf, pos, buf.length - pos)) != -1) { + pos += count; + if (pos >= buf.length) { + int size = buf.length; + if (size < 256*1024) + size += size; + else + size += 256*1024; + char tbuf[] = new char[size]; + System.arraycopy(buf, 0, tbuf, 0, pos); + buf = tbuf; + } + } + return new String(buf, 0, pos); + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore it + } + } + } + + /** + * Write the object to the output stream, using the specified MIME type. + */ + @Override + public void writeTo(Object obj, String type, OutputStream os) + throws IOException { + if (!(obj instanceof String)) + throw new IOException("\"" + getDataFlavors()[0].getMimeType() + + "\" DataContentHandler requires String object, " + + "was given object of type " + obj.getClass().toString()); + + String enc = null; + OutputStreamWriter osw = null; + + try { + enc = getCharset(type); + osw = new OutputStreamWriter(new NoCloseOutputStream(os), enc); + } catch (IllegalArgumentException iex) { + /* + * An unknown charset of the form ISO-XXX-XXX will cause + * the JDK to throw an IllegalArgumentException. The + * JDK will attempt to create a classname using this string, + * but valid classnames must not contain the character '-', + * and this results in an IllegalArgumentException, rather than + * the expected UnsupportedEncodingException. Yikes. + */ + throw new UnsupportedEncodingException(enc); + } + + String s = (String)obj; + osw.write(s, 0, s.length()); + /* + * Have to call osw.close() instead of osw.flush() because + * some charset converts, such as the iso-2022-jp converter, + * don't output the "shift out" sequence unless they're closed. + * The NoCloseOutputStream wrapper prevents the underlying + * stream from being closed. + */ + osw.close(); + } + + private String getCharset(String type) { + try { + ContentType ct = new ContentType(type); + String charset = ct.getParameter("charset"); + if (charset == null) + // If the charset parameter is absent, use US-ASCII. + charset = "us-ascii"; + return MimeUtility.javaCharset(charset); + } catch (Exception ex) { + return null; + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/handlers/text_xml.java b/fine-third-default/fine-mail/src/com/sun/mail/handlers/text_xml.java new file mode 100644 index 000000000..64438b168 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/handlers/text_xml.java @@ -0,0 +1,145 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.handlers; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.activation.ActivationDataFlavor; +import javax.activation.DataSource; +import javax.mail.internet.ContentType; +import javax.mail.internet.ParseException; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerException; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * DataContentHandler for text/xml. + * + * @author Anil Vijendran + * @author Bill Shannon + */ +public class text_xml extends text_plain { + + private static final ActivationDataFlavor[] flavors = { + new ActivationDataFlavor(String.class, "text/xml", "XML String"), + new ActivationDataFlavor(String.class, "application/xml", "XML String"), + new ActivationDataFlavor(StreamSource.class, "text/xml", "XML"), + new ActivationDataFlavor(StreamSource.class, "application/xml", "XML") + }; + + @Override + protected ActivationDataFlavor[] getDataFlavors() { + return flavors; + } + + @Override + protected Object getData(ActivationDataFlavor aFlavor, DataSource ds) + throws IOException { + if (aFlavor.getRepresentationClass() == String.class) + return super.getContent(ds); + else if (aFlavor.getRepresentationClass() == StreamSource.class) + return new StreamSource(ds.getInputStream()); + else + return null; // XXX - should never happen + } + + /** + */ + @Override + public void writeTo(Object obj, String mimeType, OutputStream os) + throws IOException { + if (!isXmlType(mimeType)) + throw new IOException( + "Invalid content type \"" + mimeType + "\" for text/xml DCH"); + if (obj instanceof String) { + super.writeTo(obj, mimeType, os); + return; + } + if (!(obj instanceof DataSource || obj instanceof Source)) { + throw new IOException("Invalid Object type = "+obj.getClass()+ + ". XmlDCH can only convert DataSource or Source to XML."); + } + + try { + Transformer transformer = + TransformerFactory.newInstance().newTransformer(); + StreamResult result = new StreamResult(os); + if (obj instanceof DataSource) { + // Streaming transform applies only to + // javax.xml.transform.StreamSource + transformer.transform( + new StreamSource(((DataSource)obj).getInputStream()), + result); + } else { + transformer.transform((Source)obj, result); + } + } catch (TransformerException ex) { + IOException ioex = new IOException( + "Unable to run the JAXP transformer on a stream " + + ex.getMessage()); + ioex.initCause(ex); + throw ioex; + } catch (RuntimeException ex) { + IOException ioex = new IOException( + "Unable to run the JAXP transformer on a stream " + + ex.getMessage()); + ioex.initCause(ex); + throw ioex; + } + } + + private boolean isXmlType(String type) { + try { + ContentType ct = new ContentType(type); + return ct.getSubType().equals("xml") && + (ct.getPrimaryType().equals("text") || + ct.getPrimaryType().equals("application")); + } catch (ParseException ex) { + return false; + } catch (RuntimeException ex) { + return false; + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/Argument.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/Argument.java new file mode 100644 index 000000000..15d190f97 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/Argument.java @@ -0,0 +1,455 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +import java.util.List; +import java.util.ArrayList; +import java.io.*; +import java.nio.charset.Charset; + +import com.sun.mail.util.ASCIIUtility; + +/** + * @author John Mani + * @author Bill Shannon + */ + +public class Argument { + protected List items; + + /** + * Constructor + */ + public Argument() { + items = new ArrayList<>(1); + } + + /** + * Append the given Argument to this Argument. All items + * from the source argument are copied into this destination + * argument. + * + * @param arg the Argument to append + * @return this + */ + public Argument append(Argument arg) { + items.addAll(arg.items); + return this; + } + + /** + * Write out given string as an ASTRING, depending on the type + * of the characters inside the string. The string should + * contain only ASCII characters.

    + * + * XXX: Hmm .. this should really be called writeASCII() + * + * @param s String to write out + * @return this + */ + public Argument writeString(String s) { + items.add(new AString(ASCIIUtility.getBytes(s))); + return this; + } + + /** + * Convert the given string into bytes in the specified + * charset, and write the bytes out as an ASTRING + * + * @param s String to write out + * @param charset the charset + * @return this + * @exception UnsupportedEncodingException for bad charset + */ + public Argument writeString(String s, String charset) + throws UnsupportedEncodingException { + if (charset == null) // convenience + writeString(s); + else + items.add(new AString(s.getBytes(charset))); + return this; + } + + /** + * Convert the given string into bytes in the specified + * charset, and write the bytes out as an ASTRING + * + * @param s String to write out + * @param charset the charset + * @return this + * @since JavaMail 1.6.0 + */ + public Argument writeString(String s, Charset charset) { + if (charset == null) // convenience + writeString(s); + else + items.add(new AString(s.getBytes(charset))); + return this; + } + + /** + * Write out given string as an NSTRING, depending on the type + * of the characters inside the string. The string should + * contain only ASCII characters.

    + * + * @param s String to write out + * @return this + * @since JavaMail 1.5.1 + */ + public Argument writeNString(String s) { + if (s == null) + items.add(new NString(null)); + else + items.add(new NString(ASCIIUtility.getBytes(s))); + return this; + } + + /** + * Convert the given string into bytes in the specified + * charset, and write the bytes out as an NSTRING + * + * @param s String to write out + * @param charset the charset + * @return this + * @exception UnsupportedEncodingException for bad charset + * @since JavaMail 1.5.1 + */ + public Argument writeNString(String s, String charset) + throws UnsupportedEncodingException { + if (s == null) + items.add(new NString(null)); + else if (charset == null) // convenience + writeString(s); + else + items.add(new NString(s.getBytes(charset))); + return this; + } + + /** + * Convert the given string into bytes in the specified + * charset, and write the bytes out as an NSTRING + * + * @param s String to write out + * @param charset the charset + * @return this + * @since JavaMail 1.6.0 + */ + public Argument writeNString(String s, Charset charset) { + if (s == null) + items.add(new NString(null)); + else if (charset == null) // convenience + writeString(s); + else + items.add(new NString(s.getBytes(charset))); + return this; + } + + /** + * Write out given byte[] as a Literal. + * @param b byte[] to write out + * @return this + */ + public Argument writeBytes(byte[] b) { + items.add(b); + return this; + } + + /** + * Write out given ByteArrayOutputStream as a Literal. + * @param b ByteArrayOutputStream to be written out. + * @return this + */ + public Argument writeBytes(ByteArrayOutputStream b) { + items.add(b); + return this; + } + + /** + * Write out given data as a literal. + * @param b Literal representing data to be written out. + * @return this + */ + public Argument writeBytes(Literal b) { + items.add(b); + return this; + } + + /** + * Write out given string as an Atom. Note that an Atom can contain only + * certain US-ASCII characters. No validation is done on the characters + * in the string. + * @param s String + * @return this + */ + public Argument writeAtom(String s) { + items.add(new Atom(s)); + return this; + } + + /** + * Write out number. + * @param i number + * @return this + */ + public Argument writeNumber(int i) { + items.add(Integer.valueOf(i)); + return this; + } + + /** + * Write out number. + * @param i number + * @return this + */ + public Argument writeNumber(long i) { + items.add(Long.valueOf(i)); + return this; + } + + /** + * Write out as parenthesised list. + * + * @param c the Argument + * @return this + */ + public Argument writeArgument(Argument c) { + items.add(c); + return this; + } + + /* + * Write out all the buffered items into the output stream. + */ + public void write(Protocol protocol) + throws IOException, ProtocolException { + int size = items != null ? items.size() : 0; + DataOutputStream os = (DataOutputStream)protocol.getOutputStream(); + + for (int i=0; i < size; i++) { + if (i > 0) // write delimiter if not the first item + os.write(' '); + + Object o = items.get(i); + if (o instanceof Atom) { + os.writeBytes(((Atom)o).string); + } else if (o instanceof Number) { + os.writeBytes(((Number)o).toString()); + } else if (o instanceof AString) { + astring(((AString)o).bytes, protocol); + } else if (o instanceof NString) { + nstring(((NString)o).bytes, protocol); + } else if (o instanceof byte[]) { + literal((byte[])o, protocol); + } else if (o instanceof ByteArrayOutputStream) { + literal((ByteArrayOutputStream)o, protocol); + } else if (o instanceof Literal) { + literal((Literal)o, protocol); + } else if (o instanceof Argument) { + os.write('('); // open parans + ((Argument)o).write(protocol); + os.write(')'); // close parans + } + } + } + + /** + * Write out given String as either an Atom, QuotedString or Literal + */ + private void astring(byte[] bytes, Protocol protocol) + throws IOException, ProtocolException { + nastring(bytes, protocol, false); + } + + /** + * Write out given String as either NIL, QuotedString, or Literal. + */ + private void nstring(byte[] bytes, Protocol protocol) + throws IOException, ProtocolException { + if (bytes == null) { + DataOutputStream os = (DataOutputStream)protocol.getOutputStream(); + os.writeBytes("NIL"); + } else + nastring(bytes, protocol, true); + } + + private void nastring(byte[] bytes, Protocol protocol, boolean doQuote) + throws IOException, ProtocolException { + DataOutputStream os = (DataOutputStream)protocol.getOutputStream(); + int len = bytes.length; + + // If length is greater than 1024 bytes, send as literal + if (len > 1024) { + literal(bytes, protocol); + return; + } + + // if 0 length, send as quoted-string + boolean quote = len == 0 ? true : doQuote; + boolean escape = false; + boolean utf8 = protocol.supportsUtf8(); + + byte b; + for (int i = 0; i < len; i++) { + b = bytes[i]; + if (b == '\0' || b == '\r' || b == '\n' || + (!utf8 && ((b & 0xff) > 0177))) { + // NUL, CR or LF means the bytes need to be sent as literals + literal(bytes, protocol); + return; + } + if (b == '*' || b == '%' || b == '(' || b == ')' || b == '{' || + b == '"' || b == '\\' || + ((b & 0xff) <= ' ') || ((b & 0xff) > 0177)) { + quote = true; + if (b == '"' || b == '\\') // need to escape these characters + escape = true; + } + } + + /* + * Make sure the (case-independent) string "NIL" is always quoted, + * so as not to be confused with a real NIL (handled above in nstring). + * This is more than is necessary, but it's rare to begin with and + * this makes it safer than doing the test in nstring above in case + * some code calls writeString when it should call writeNString. + */ + if (!quote && bytes.length == 3 && + (bytes[0] == 'N' || bytes[0] == 'n') && + (bytes[1] == 'I' || bytes[1] == 'i') && + (bytes[2] == 'L' || bytes[2] == 'l')) + quote = true; + + if (quote) // start quote + os.write('"'); + + if (escape) { + // already quoted + for (int i = 0; i < len; i++) { + b = bytes[i]; + if (b == '"' || b == '\\') + os.write('\\'); + os.write(b); + } + } else + os.write(bytes); + + + if (quote) // end quote + os.write('"'); + } + + /** + * Write out given byte[] as a literal + */ + private void literal(byte[] b, Protocol protocol) + throws IOException, ProtocolException { + startLiteral(protocol, b.length).write(b); + } + + /** + * Write out given ByteArrayOutputStream as a literal. + */ + private void literal(ByteArrayOutputStream b, Protocol protocol) + throws IOException, ProtocolException { + b.writeTo(startLiteral(protocol, b.size())); + } + + /** + * Write out given Literal as a literal. + */ + private void literal(Literal b, Protocol protocol) + throws IOException, ProtocolException { + b.writeTo(startLiteral(protocol, b.size())); + } + + private OutputStream startLiteral(Protocol protocol, int size) + throws IOException, ProtocolException { + DataOutputStream os = (DataOutputStream)protocol.getOutputStream(); + boolean nonSync = protocol.supportsNonSyncLiterals(); + + os.write('{'); + os.writeBytes(Integer.toString(size)); + if (nonSync) // server supports non-sync literals + os.writeBytes("+}\r\n"); + else + os.writeBytes("}\r\n"); + os.flush(); + + // If we are using synchronized literals, wait for the server's + // continuation signal + if (!nonSync) { + for (; ;) { + Response r = protocol.readResponse(); + if (r.isContinuation()) + break; + if (r.isTagged()) + throw new LiteralException(r); + // XXX - throw away untagged responses; + // violates IMAP spec, hope no servers do this + } + } + return os; + } +} + +class Atom { + String string; + + Atom(String s) { + string = s; + } +} + +class AString { + byte[] bytes; + + AString(byte[] b) { + bytes = b; + } +} + +class NString { + byte[] bytes; + + NString(byte[] b) { + bytes = b; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/BadCommandException.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/BadCommandException.java new file mode 100644 index 000000000..dde7b5bf8 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/BadCommandException.java @@ -0,0 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +/** + * @author John Mani + */ + +public class BadCommandException extends ProtocolException { + + private static final long serialVersionUID = 5769722539397237515L; + + /** + * Constructs an BadCommandException with no detail message. + */ + public BadCommandException() { + super(); + } + + /** + * Constructs an BadCommandException with the specified detail message. + * @param s the detail message + */ + public BadCommandException(String s) { + super(s); + } + + /** + * Constructs an BadCommandException with the specified Response. + * @param r the Response + */ + public BadCommandException(Response r) { + super(r); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/ByteArray.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/ByteArray.java new file mode 100644 index 000000000..bf8ce60dc --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/ByteArray.java @@ -0,0 +1,149 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +import java.io.ByteArrayInputStream; + +/** + * A simple wrapper around a byte array, with a start position and + * count of bytes. + * + * @author John Mani + */ + +public class ByteArray { + private byte[] bytes; // the byte array + private int start; // start position + private int count; // count of bytes + + /** + * Constructor + * + * @param b the byte array to wrap + * @param start start position in byte array + * @param count number of bytes in byte array + */ + public ByteArray(byte[] b, int start, int count) { + bytes = b; + this.start = start; + this.count = count; + } + + /** + * Constructor that creates a byte array of the specified size. + * + * @param size the size of the ByteArray + * @since JavaMail 1.4.1 + */ + public ByteArray(int size) { + this(new byte[size], 0, size); + } + + /** + * Returns the internal byte array. Note that this is a live + * reference to the actual data, not a copy. + * + * @return the wrapped byte array + */ + public byte[] getBytes() { + return bytes; + } + + /** + * Returns a new byte array that is a copy of the data. + * + * @return a new byte array with the bytes from start for count + */ + public byte[] getNewBytes() { + byte[] b = new byte[count]; + System.arraycopy(bytes, start, b, 0, count); + return b; + } + + /** + * Returns the start position + * + * @return the start position + */ + public int getStart() { + return start; + } + + /** + * Returns the count of bytes + * + * @return the number of bytes + */ + public int getCount() { + return count; + } + + /** + * Set the count of bytes. + * + * @param count the number of bytes + * @since JavaMail 1.4.1 + */ + public void setCount(int count) { + this.count = count; + } + + /** + * Returns a ByteArrayInputStream. + * + * @return the ByteArrayInputStream + */ + public ByteArrayInputStream toByteArrayInputStream() { + return new ByteArrayInputStream(bytes, start, count); + } + + /** + * Grow the byte array by incr bytes. + * + * @param incr how much to grow + * @since JavaMail 1.4.1 + */ + public void grow(int incr) { + byte[] nbuf = new byte[bytes.length + incr]; + System.arraycopy(bytes, 0, nbuf, 0, bytes.length); + bytes = nbuf; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/CommandFailedException.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/CommandFailedException.java new file mode 100644 index 000000000..aeb630c3f --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/CommandFailedException.java @@ -0,0 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +/** + * @author John Mani + */ + +public class CommandFailedException extends ProtocolException { + + private static final long serialVersionUID = 793932807880443631L; + + /** + * Constructs an CommandFailedException with no detail message. + */ + public CommandFailedException() { + super(); + } + + /** + * Constructs an CommandFailedException with the specified detail message. + * @param s the detail message + */ + public CommandFailedException(String s) { + super(s); + } + + /** + * Constructs an CommandFailedException with the specified Response. + * @param r the Response. + */ + public CommandFailedException(Response r) { + super(r); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/ConnectionException.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/ConnectionException.java new file mode 100644 index 000000000..187b8528d --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/ConnectionException.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +/** + * @author John Mani + */ + +public class ConnectionException extends ProtocolException { + private transient Protocol p; + + private static final long serialVersionUID = 5749739604257464727L; + + /** + * Constructs an ConnectionException with no detail message. + */ + public ConnectionException() { + super(); + } + + /** + * Constructs an ConnectionException with the specified detail message. + * + * @param s the detail message + */ + public ConnectionException(String s) { + super(s); + } + + /** + * Constructs an ConnectionException with the specified Response. + * + * @param p the Protocol object + * @param r the Response + */ + public ConnectionException(Protocol p, Response r) { + super(r); + this.p = p; + } + + public Protocol getProtocol() { + return p; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/Literal.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/Literal.java new file mode 100644 index 000000000..44706b068 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/Literal.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +import java.io.*; + +/** + * An interface for objects that provide data dynamically for use in + * a literal protocol element. + * + * @author Bill Shannon + */ + +public interface Literal { + /** + * Return the size of the data. + * + * @return the size of the data + */ + public int size(); + + /** + * Write the data to the OutputStream. + * + * @param os the output stream + * @exception IOException for I/O errors + */ + public void writeTo(OutputStream os) throws IOException; +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/LiteralException.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/LiteralException.java new file mode 100644 index 000000000..ff12d4907 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/LiteralException.java @@ -0,0 +1,60 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +/** + * @author Bill Shannon + */ + +public class LiteralException extends ProtocolException { + + private static final long serialVersionUID = -6919179828339609913L; + + /** + * Constructs a LiteralException with the specified Response object. + * + * @param r the response object + */ + public LiteralException(Response r) { + super(r.toString()); + response = r; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/ParsingException.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/ParsingException.java new file mode 100644 index 000000000..c947609b7 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/ParsingException.java @@ -0,0 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +/** + * @author John Mani + */ + +public class ParsingException extends ProtocolException { + + private static final long serialVersionUID = 7756119840142724839L; + + /** + * Constructs an ParsingException with no detail message. + */ + public ParsingException() { + super(); + } + + /** + * Constructs an ParsingException with the specified detail message. + * @param s the detail message + */ + public ParsingException(String s) { + super(s); + } + + /** + * Constructs an ParsingException with the specified Response. + * @param r the Response + */ + public ParsingException(Response r) { + super(r); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/Protocol.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/Protocol.java new file mode 100644 index 000000000..bed9ef053 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/Protocol.java @@ -0,0 +1,695 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +import java.util.Properties; +import java.io.*; +import java.nio.channels.SocketChannel; +import java.net.*; +import javax.net.ssl.SSLSocket; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; + +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.MailLogger; +import com.sun.mail.util.SocketFetcher; +import com.sun.mail.util.TraceInputStream; +import com.sun.mail.util.TraceOutputStream; + +/** + * General protocol handling code for IMAP-like protocols.

    + * + * The Protocol object is multithread safe. + * + * @author John Mani + * @author Max Spivak + * @author Bill Shannon + */ + +public class Protocol { + protected String host; + private Socket socket; + // in case we turn on TLS, we'll need these later + protected boolean quote; + protected MailLogger logger; + protected MailLogger traceLogger; + protected Properties props; + protected String prefix; + + private TraceInputStream traceInput; // the Tracer + private volatile ResponseInputStream input; + + private TraceOutputStream traceOutput; // the Tracer + private volatile DataOutputStream output; + + private int tagCounter = 0; + private final String tagPrefix; + + private String localHostName; + + private final List handlers + = new CopyOnWriteArrayList<>(); + + private volatile long timestamp; + + // package private, to allow testing + static final AtomicInteger tagNum = new AtomicInteger(); + + private static final byte[] CRLF = { (byte)'\r', (byte)'\n'}; + + /** + * Constructor.

    + * + * Opens a connection to the given host at given port. + * + * @param host host to connect to + * @param port portnumber to connect to + * @param props Properties object used by this protocol + * @param prefix Prefix to prepend to property keys + * @param isSSL use SSL? + * @param logger log messages here + * @exception IOException for I/O errors + * @exception ProtocolException for protocol failures + */ + public Protocol(String host, int port, + Properties props, String prefix, + boolean isSSL, MailLogger logger) + throws IOException, ProtocolException { + boolean connected = false; // did constructor succeed? + tagPrefix = computePrefix(props, prefix); + try { + this.host = host; + this.props = props; + this.prefix = prefix; + this.logger = logger; + traceLogger = logger.getSubLogger("protocol", null); + + socket = SocketFetcher.getSocket(host, port, props, prefix, isSSL); + quote = PropUtil.getBooleanProperty(props, + "mail.debug.quote", false); + + initStreams(); + + // Read server greeting + processGreeting(readResponse()); + + timestamp = System.currentTimeMillis(); + + connected = true; // must be last statement in constructor + } finally { + /* + * If we get here because an exception was thrown, we need + * to disconnect to avoid leaving a connected socket that + * no one will be able to use because this object was never + * completely constructed. + */ + if (!connected) + disconnect(); + } + } + + private void initStreams() throws IOException { + traceInput = new TraceInputStream(socket.getInputStream(), traceLogger); + traceInput.setQuote(quote); + input = new ResponseInputStream(traceInput); + + traceOutput = + new TraceOutputStream(socket.getOutputStream(), traceLogger); + traceOutput.setQuote(quote); + output = new DataOutputStream(new BufferedOutputStream(traceOutput)); + } + + /** + * Compute the tag prefix to be used for this connection. + * Start with "A" - "Z", then "AA" - "ZZ", and finally "AAA" - "ZZZ". + * Wrap around after that. + */ + private String computePrefix(Properties props, String prefix) { + // XXX - in case someone depends on the tag prefix + if (PropUtil.getBooleanProperty(props, + prefix + ".reusetagprefix", false)) + return "A"; + // tag prefix, wrap around after three letters + int n = tagNum.getAndIncrement() % (26*26*26 + 26*26 + 26); + String tagPrefix; + if (n < 26) + tagPrefix = new String(new char[] { (char)('A' + n) }); + else if (n < (26*26 + 26)) { + n -= 26; + tagPrefix = new String(new char[] { + (char)('A' + n/26), (char)('A' + n%26) }); + } else { + n -= (26*26 + 26); + tagPrefix = new String(new char[] { + (char)('A' + n/(26*26)), + (char)('A' + (n%(26*26))/26), + (char)('A' + n%26) }); + } + return tagPrefix; + } + + /** + * Constructor for debugging. + * + * @param in the InputStream to read from + * @param out the PrintStream to write to + * @param props Properties object used by this protocol + * @param debug true to enable debugging output + * @exception IOException for I/O errors + */ + public Protocol(InputStream in, PrintStream out, Properties props, + boolean debug) throws IOException { + this.host = "localhost"; + this.props = props; + this.quote = false; + tagPrefix = computePrefix(props, "mail.imap"); + logger = new MailLogger(this.getClass(), "DEBUG", debug, System.out); + traceLogger = logger.getSubLogger("protocol", null); + + // XXX - inlined initStreams, won't allow later startTLS + traceInput = new TraceInputStream(in, traceLogger); + traceInput.setQuote(quote); + input = new ResponseInputStream(traceInput); + + traceOutput = new TraceOutputStream(out, traceLogger); + traceOutput.setQuote(quote); + output = new DataOutputStream(new BufferedOutputStream(traceOutput)); + + timestamp = System.currentTimeMillis(); + } + + /** + * Returns the timestamp. + * + * @return the timestamp + */ + public long getTimestamp() { + return timestamp; + } + + /** + * Adds a response handler. + * + * @param h the response handler + */ + public void addResponseHandler(ResponseHandler h) { + handlers.add(h); + } + + /** + * Removed the specified response handler. + * + * @param h the response handler + */ + public void removeResponseHandler(ResponseHandler h) { + handlers.remove(h); + } + + /** + * Notify response handlers + * + * @param responses the responses + */ + public void notifyResponseHandlers(Response[] responses) { + if (handlers.isEmpty()) { + return; + } + + for (Response r : responses) { + if (r != null) { + for (ResponseHandler rh : handlers) { + if (rh != null) { + rh.handleResponse(r); + } + } + } + } + } + + protected void processGreeting(Response r) throws ProtocolException { + if (r.isBYE()) + throw new ConnectionException(this, r); + } + + /** + * Return the Protocol's InputStream. + * + * @return the input stream + */ + protected ResponseInputStream getInputStream() { + return input; + } + + /** + * Return the Protocol's OutputStream + * + * @return the output stream + */ + protected OutputStream getOutputStream() { + return output; + } + + /** + * Returns whether this Protocol supports non-synchronizing literals + * Default is false. Subclasses should override this if required + * + * @return true if the server supports non-synchronizing literals + */ + protected synchronized boolean supportsNonSyncLiterals() { + return false; + } + + public Response readResponse() + throws IOException, ProtocolException { + return new Response(this); + } + + /** + * Is another response available in our buffer? + * + * @return true if another response is in the buffer + * @since JavaMail 1.5.4 + */ + public boolean hasResponse() { + /* + * XXX - Really should peek ahead in the buffer to see + * if there's a *complete* response available, but if there + * isn't who's going to read more data into the buffer + * until there is? + */ + try { + return input.available() > 0; + } catch (IOException ex) { + } + return false; + } + + /** + * Return a buffer to be used to read a response. + * The default implementation returns null, which causes + * a new buffer to be allocated for every response. + * + * @return the buffer to use + * @since JavaMail 1.4.1 + */ + protected ByteArray getResponseBuffer() { + return null; + } + + public String writeCommand(String command, Argument args) + throws IOException, ProtocolException { + // assert Thread.holdsLock(this); + // can't assert because it's called from constructor + String tag = tagPrefix + Integer.toString(tagCounter++); // unique tag + + output.writeBytes(tag + " " + command); + + if (args != null) { + output.write(' '); + args.write(this); + } + + output.write(CRLF); + output.flush(); + return tag; + } + + /** + * Send a command to the server. Collect all responses until either + * the corresponding command completion response or a BYE response + * (indicating server failure). Return all the collected responses. + * + * @param command the command + * @param args the arguments + * @return array of Response objects returned by the server + */ + public synchronized Response[] command(String command, Argument args) { + commandStart(command); + List v = new ArrayList<>(); + boolean done = false; + String tag = null; + + // write the command + try { + tag = writeCommand(command, args); + } catch (LiteralException lex) { + v.add(lex.getResponse()); + done = true; + } catch (Exception ex) { + // Convert this into a BYE response + v.add(Response.byeResponse(ex)); + done = true; + } + + Response byeResp = null; + while (!done) { + Response r = null; + try { + r = readResponse(); + } catch (IOException ioex) { + if (byeResp == null) // convert this into a BYE response + byeResp = Response.byeResponse(ioex); + // else, connection closed after BYE was sent + break; + } catch (ProtocolException pex) { + logger.log(Level.FINE, "ignoring bad response", pex); + continue; // skip this response + } + + if (r.isBYE()) { + byeResp = r; + continue; + } + + v.add(r); + + // If this is a matching command completion response, we are done + if (r.isTagged() && r.getTag().equals(tag)) + done = true; + } + + if (byeResp != null) + v.add(byeResp); // must be last + Response[] responses = new Response[v.size()]; + v.toArray(responses); + timestamp = System.currentTimeMillis(); + commandEnd(); + return responses; + } + + /** + * Convenience routine to handle OK, NO, BAD and BYE responses. + * + * @param response the response + * @exception ProtocolException for protocol failures + */ + public void handleResult(Response response) throws ProtocolException { + if (response.isOK()) + return; + else if (response.isNO()) + throw new CommandFailedException(response); + else if (response.isBAD()) + throw new BadCommandException(response); + else if (response.isBYE()) { + disconnect(); + throw new ConnectionException(this, response); + } + } + + /** + * Convenience routine to handle simple IAP commands + * that do not have responses specific to that command. + * + * @param cmd the command + * @param args the arguments + * @exception ProtocolException for protocol failures + */ + public void simpleCommand(String cmd, Argument args) + throws ProtocolException { + // Issue command + Response[] r = command(cmd, args); + + // dispatch untagged responses + notifyResponseHandlers(r); + + // Handle result of this command + handleResult(r[r.length-1]); + } + + /** + * Start TLS on the current connection. + * cmd is the command to issue to start TLS negotiation. + * If the command succeeds, we begin TLS negotiation. + * If the socket is already an SSLSocket this is a nop and the command + * is not issued. + * + * @param cmd the command to issue + * @exception IOException for I/O errors + * @exception ProtocolException for protocol failures + */ + public synchronized void startTLS(String cmd) + throws IOException, ProtocolException { + if (socket instanceof SSLSocket) + return; // nothing to do + simpleCommand(cmd, null); + socket = SocketFetcher.startTLS(socket, host, props, prefix); + initStreams(); + } + + /** + * Start compression on the current connection. + * cmd is the command to issue to start compression. + * If the command succeeds, we begin compression. + * + * @param cmd the command to issue + * @exception IOException for I/O errors + * @exception ProtocolException for protocol failures + */ + public synchronized void startCompression(String cmd) + throws IOException, ProtocolException { + // XXX - check whether compression is already enabled? + simpleCommand(cmd, null); + + // need to create our own Inflater and Deflater in order to set nowrap + Inflater inf = new Inflater(true); + traceInput = new TraceInputStream(new InflaterInputStream( + socket.getInputStream(), inf), traceLogger); + traceInput.setQuote(quote); + input = new ResponseInputStream(traceInput); + + // configure the Deflater + int level = PropUtil.getIntProperty(props, prefix + ".compress.level", + Deflater.DEFAULT_COMPRESSION); + int strategy = PropUtil.getIntProperty(props, + prefix + ".compress.strategy", + Deflater.DEFAULT_STRATEGY); + if (logger.isLoggable(Level.FINE)) + logger.log(Level.FINE, + "Creating Deflater with compression level {0} and strategy {1}", + new Object[] { level, strategy }); + Deflater def = new Deflater(Deflater.DEFAULT_COMPRESSION, true); + try { + def.setLevel(level); + } catch (IllegalArgumentException ex) { + logger.log(Level.FINE, "Ignoring bad compression level", ex); + } + try { + def.setStrategy(strategy); + } catch (IllegalArgumentException ex) { + logger.log(Level.FINE, "Ignoring bad compression strategy", ex); + } + traceOutput = new TraceOutputStream(new DeflaterOutputStream( + socket.getOutputStream(), def, true), traceLogger); + traceOutput.setQuote(quote); + output = new DataOutputStream(new BufferedOutputStream(traceOutput)); + } + + /** + * Is this connection using an SSL socket? + * + * @return true if using SSL + * @since JavaMail 1.4.6 + */ + public boolean isSSL() { + return socket instanceof SSLSocket; + } + + /** + * Return the address the socket connected to. + * + * @return the InetAddress the socket is connected to + * @since JavaMail 1.5.2 + */ + public InetAddress getInetAddress() { + return socket.getInetAddress(); + } + + /** + * Return the SocketChannel associated with this connection, if any. + * + * @return the SocketChannel + * @since JavaMail 1.5.2 + */ + public SocketChannel getChannel() { + SocketChannel ret = socket.getChannel(); + if (ret != null) + return ret; + + // XXX - Android is broken and SSL wrapped sockets don't delegate + // the getChannel method to the wrapped Socket + if (socket instanceof SSLSocket) { + try { + Field f = socket.getClass().getDeclaredField("socket"); + f.setAccessible(true); + Socket s = (Socket)f.get(socket); + ret = s.getChannel(); + } catch (Exception ex) { + // ignore anything that might go wrong + } + } + return ret; + } + + /** + * Does the server support UTF-8? + * This implementation returns false. + * Subclasses should override as appropriate. + * + * @return true if the server supports UTF-8 + * @since JavaMail 1.6.0 + */ + public boolean supportsUtf8() { + return false; + } + + /** + * Disconnect. + */ + protected synchronized void disconnect() { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + // ignore it + } + socket = null; + } + } + + /** + * Get the name of the local host. + * The property <prefix>.localhost overrides + * <prefix>.localaddress, + * which overrides what InetAddress would tell us. + * + * @return the name of the local host + */ + protected synchronized String getLocalHost() { + // get our hostname and cache it for future use + if (localHostName == null || localHostName.length() <= 0) + localHostName = + props.getProperty(prefix + ".localhost"); + if (localHostName == null || localHostName.length() <= 0) + localHostName = + props.getProperty(prefix + ".localaddress"); + try { + if (localHostName == null || localHostName.length() <= 0) { + InetAddress localHost = InetAddress.getLocalHost(); + localHostName = localHost.getCanonicalHostName(); + // if we can't get our name, use local address literal + if (localHostName == null) + // XXX - not correct for IPv6 + localHostName = "[" + localHost.getHostAddress() + "]"; + } + } catch (UnknownHostException uhex) { + } + + // last chance, try to get our address from our socket + if (localHostName == null || localHostName.length() <= 0) { + if (socket != null && socket.isBound()) { + InetAddress localHost = socket.getLocalAddress(); + localHostName = localHost.getCanonicalHostName(); + // if we can't get our name, use local address literal + if (localHostName == null) + // XXX - not correct for IPv6 + localHostName = "[" + localHost.getHostAddress() + "]"; + } + } + return localHostName; + } + + /** + * Is protocol tracing enabled? + * + * @return true if protocol tracing is enabled + */ + protected boolean isTracing() { + return traceLogger.isLoggable(Level.FINEST); + } + + /** + * Temporarily turn off protocol tracing, e.g., to prevent + * tracing the authentication sequence, including the password. + */ + protected void suspendTracing() { + if (traceLogger.isLoggable(Level.FINEST)) { + traceInput.setTrace(false); + traceOutput.setTrace(false); + } + } + + /** + * Resume protocol tracing, if it was enabled to begin with. + */ + protected void resumeTracing() { + if (traceLogger.isLoggable(Level.FINEST)) { + traceInput.setTrace(true); + traceOutput.setTrace(true); + } + } + + /** + * Finalizer. + */ + @Override + protected void finalize() throws Throwable { + try { + disconnect(); + } finally { + super.finalize(); + } + } + + /* + * Probe points for GlassFish monitoring. + */ + private void commandStart(String command) { } + private void commandEnd() { } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/ProtocolException.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/ProtocolException.java new file mode 100644 index 000000000..b7e366ab2 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/ProtocolException.java @@ -0,0 +1,97 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +/** + * @author John Mani + */ + +public class ProtocolException extends Exception { + protected transient Response response = null; + + private static final long serialVersionUID = -4360500807971797439L; + + /** + * Constructs a ProtocolException with no detail message. + */ + public ProtocolException() { + super(); + } + + /** + * Constructs a ProtocolException with the specified detail message. + * + * @param message the detail message + */ + public ProtocolException(String message) { + super(message); + } + + /** + * Constructs a ProtocolException with the specified detail message + * and cause. + * + * @param message the detail message + * @param cause the cause + */ + public ProtocolException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a ProtocolException with the specified Response object. + * + * @param r the Response + */ + public ProtocolException(Response r) { + super(r.toString()); + response = r; + } + + /** + * Return the offending Response object. + * + * @return the Response object + */ + public Response getResponse() { + return response; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/Response.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/Response.java new file mode 100644 index 000000000..0225d9a82 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/Response.java @@ -0,0 +1,624 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +import java.io.*; +import java.util.*; +import java.nio.charset.StandardCharsets; + +import com.sun.mail.util.ASCIIUtility; + +/** + * This class represents a response obtained from the input stream + * of an IMAP server. + * + * @author John Mani + * @author Bill Shannon + */ + +public class Response { + protected int index; // internal index (updated during the parse) + protected int pindex; // index after parse, for reset + protected int size; // number of valid bytes in our buffer + protected byte[] buffer = null; + protected int type = 0; + protected String tag = null; + /** @since JavaMail 1.5.4 */ + protected Exception ex; + protected boolean utf8; + + private static final int increment = 100; + + // The first and second bits indicate whether this response + // is a Continuation, Tagged or Untagged + public final static int TAG_MASK = 0x03; + public final static int CONTINUATION = 0x01; + public final static int TAGGED = 0x02; + public final static int UNTAGGED = 0x03; + + // The third, fourth and fifth bits indicate whether this response + // is an OK, NO, BAD or BYE response + public final static int TYPE_MASK = 0x1C; + public final static int OK = 0x04; + public final static int NO = 0x08; + public final static int BAD = 0x0C; + public final static int BYE = 0x10; + + // The sixth bit indicates whether a BYE response is synthetic or real + public final static int SYNTHETIC = 0x20; + + /** + * An ATOM is any CHAR delimited by: + * SPACE | CTL | '(' | ')' | '{' | '%' | '*' | '"' | '\' | ']' + * (CTL is handled in readDelimString.) + */ + private static String ATOM_CHAR_DELIM = " (){%*\"\\]"; + + /** + * An ASTRING_CHAR is any CHAR delimited by: + * SPACE | CTL | '(' | ')' | '{' | '%' | '*' | '"' | '\' + * (CTL is handled in readDelimString.) + */ + private static String ASTRING_CHAR_DELIM = " (){%*\"\\"; + + public Response(String s) { + this(s, true); + } + + /** + * Constructor for testing. + * + * @param s the response string + * @param supportsUtf8 allow UTF-8 in response? + * @since JavaMail 1.6.0 + */ + public Response(String s, boolean supportsUtf8) { + if (supportsUtf8) + buffer = s.getBytes(StandardCharsets.UTF_8); + else + buffer = s.getBytes(StandardCharsets.US_ASCII); + size = buffer.length; + utf8 = supportsUtf8; + parse(); + } + + /** + * Read a new Response from the given Protocol + * + * @param p the Protocol object + * @exception IOException for I/O errors + * @exception ProtocolException for protocol failures + */ + public Response(Protocol p) throws IOException, ProtocolException { + // read one response into 'buffer' + ByteArray ba = p.getResponseBuffer(); + ByteArray response = p.getInputStream().readResponse(ba); + buffer = response.getBytes(); + size = response.getCount() - 2; // Skip the terminating CRLF + utf8 = p.supportsUtf8(); + + parse(); + } + + /** + * Copy constructor. + * + * @param r the Response to copy + */ + public Response(Response r) { + index = r.index; + pindex = r.pindex; + size = r.size; + buffer = r.buffer; + type = r.type; + tag = r.tag; + ex = r.ex; + utf8 = r.utf8; + } + + /** + * Return a Response object that looks like a BYE protocol response. + * Include the details of the exception in the response string. + * + * @param ex the exception + * @return the synthetic Response object + */ + public static Response byeResponse(Exception ex) { + String err = "* BYE JavaMail Exception: " + ex.toString(); + err = err.replace('\r', ' ').replace('\n', ' '); + Response r = new Response(err); + r.type |= SYNTHETIC; + r.ex = ex; + return r; + } + + /** + * Does the server support UTF-8? + * + * @return true if the server supports UTF-8 + * @since JavaMail 1.6.0 + */ + public boolean supportsUtf8() { + return utf8; + } + + private void parse() { + index = 0; // position internal index at start + + if (size == 0) // empty line + return; + if (buffer[index] == '+') { // Continuation statement + type |= CONTINUATION; + index += 1; // Position beyond the '+' + return; // return + } else if (buffer[index] == '*') { // Untagged statement + type |= UNTAGGED; + index += 1; // Position beyond the '*' + } else { // Tagged statement + type |= TAGGED; + tag = readAtom(); // read the TAG, index positioned beyond tag + if (tag == null) + tag = ""; // avoid possible NPE + } + + int mark = index; // mark + String s = readAtom(); // updates index + if (s == null) + s = ""; // avoid possible NPE + if (s.equalsIgnoreCase("OK")) + type |= OK; + else if (s.equalsIgnoreCase("NO")) + type |= NO; + else if (s.equalsIgnoreCase("BAD")) + type |= BAD; + else if (s.equalsIgnoreCase("BYE")) + type |= BYE; + else + index = mark; // reset + + pindex = index; + return; + } + + public void skipSpaces() { + while (index < size && buffer[index] == ' ') + index++; + } + + /** + * Skip past any spaces. If the next non-space character is c, + * consume it and return true. Otherwise stop at that point + * and return false. + * + * @param c the character to look for + * @return true if the character is found + */ + public boolean isNextNonSpace(char c) { + skipSpaces(); + if (index < size && buffer[index] == (byte)c) { + index++; + return true; + } + return false; + } + + /** + * Skip to the next space, for use in error recovery while parsing. + */ + public void skipToken() { + while (index < size && buffer[index] != ' ') + index++; + } + + public void skip(int count) { + index += count; + } + + public byte peekByte() { + if (index < size) + return buffer[index]; + else + return 0; // XXX - how else to signal error? + } + + /** + * Return the next byte from this Statement. + * + * @return the next byte + */ + public byte readByte() { + if (index < size) + return buffer[index++]; + else + return 0; // XXX - how else to signal error? + } + + /** + * Extract an ATOM, starting at the current position. Updates + * the internal index to beyond the Atom. + * + * @return an Atom + */ + public String readAtom() { + return readDelimString(ATOM_CHAR_DELIM); + } + + /** + * Extract a string stopping at control characters or any + * character in delim. + */ + private String readDelimString(String delim) { + skipSpaces(); + + if (index >= size) // already at end of response + return null; + + int b; + int start = index; + while (index < size && ((b = (((int)buffer[index])&0xff)) >= ' ') && + delim.indexOf((char)b) < 0 && b != 0x7f) + index++; + + return toString(buffer, start, index); + } + + /** + * Read a string as an arbitrary sequence of characters, + * stopping at the delimiter Used to read part of a + * response code inside []. + * + * @param delim the delimiter character + * @return the string + */ + public String readString(char delim) { + skipSpaces(); + + if (index >= size) // already at end of response + return null; + + int start = index; + while (index < size && buffer[index] != delim) + index++; + + return toString(buffer, start, index); + } + + public String[] readStringList() { + return readStringList(false); + } + + public String[] readAtomStringList() { + return readStringList(true); + } + + private String[] readStringList(boolean atom) { + skipSpaces(); + + if (buffer[index] != '(') { // not what we expected + return null; + } + index++; // skip '(' + + // to handle buggy IMAP servers, we tolerate multiple spaces as + // well as spaces after the left paren or before the right paren + List result = new ArrayList<>(); + while (!isNextNonSpace(')')) { + String s = atom ? readAtomString() : readString(); + if (s == null) // not the expected string or atom + break; + result.add(s); + } + + return result.toArray(new String[result.size()]); + } + + /** + * Extract an integer, starting at the current position. Updates the + * internal index to beyond the number. Returns -1 if a number was + * not found. + * + * @return a number + */ + public int readNumber() { + // Skip leading spaces + skipSpaces(); + + int start = index; + while (index < size && Character.isDigit((char)buffer[index])) + index++; + + if (index > start) { + try { + return ASCIIUtility.parseInt(buffer, start, index); + } catch (NumberFormatException nex) { } + } + + return -1; + } + + /** + * Extract a long number, starting at the current position. Updates the + * internal index to beyond the number. Returns -1 if a long number + * was not found. + * + * @return a long + */ + public long readLong() { + // Skip leading spaces + skipSpaces(); + + int start = index; + while (index < size && Character.isDigit((char)buffer[index])) + index++; + + if (index > start) { + try { + return ASCIIUtility.parseLong(buffer, start, index); + } catch (NumberFormatException nex) { } + } + + return -1; + } + + /** + * Extract a NSTRING, starting at the current position. Return it as + * a String. The sequence 'NIL' is returned as null + * + * NSTRING := QuotedString | Literal | "NIL" + * + * @return a String + */ + public String readString() { + return (String)parseString(false, true); + } + + /** + * Extract a NSTRING, starting at the current position. Return it as + * a ByteArrayInputStream. The sequence 'NIL' is returned as null + * + * NSTRING := QuotedString | Literal | "NIL" + * + * @return a ByteArrayInputStream + */ + public ByteArrayInputStream readBytes() { + ByteArray ba = readByteArray(); + if (ba != null) + return ba.toByteArrayInputStream(); + else + return null; + } + + /** + * Extract a NSTRING, starting at the current position. Return it as + * a ByteArray. The sequence 'NIL' is returned as null + * + * NSTRING := QuotedString | Literal | "NIL" + * + * @return a ByteArray + */ + public ByteArray readByteArray() { + /* + * Special case, return the data after the continuation uninterpreted. + * It's usually a challenge for an AUTHENTICATE command. + */ + if (isContinuation()) { + skipSpaces(); + return new ByteArray(buffer, index, size - index); + } + return (ByteArray)parseString(false, false); + } + + /** + * Extract an ASTRING, starting at the current position + * and return as a String. An ASTRING can be a QuotedString, a + * Literal or an Atom (plus ']'). + * + * Any errors in parsing returns null + * + * ASTRING := QuotedString | Literal | 1*ASTRING_CHAR + * + * @return a String + */ + public String readAtomString() { + return (String)parseString(true, true); + } + + /** + * Generic parsing routine that can parse out a Quoted-String, + * Literal or Atom and return the parsed token as a String + * or a ByteArray. Errors or NIL data will return null. + */ + private Object parseString(boolean parseAtoms, boolean returnString) { + byte b; + + // Skip leading spaces + skipSpaces(); + + b = buffer[index]; + if (b == '"') { // QuotedString + index++; // skip the quote + int start = index; + int copyto = index; + + while (index < size && (b = buffer[index]) != '"') { + if (b == '\\') // skip escaped byte + index++; + if (index != copyto) { // only copy if we need to + // Beware: this is a destructive copy. I'm + // pretty sure this is OK, but ... ;> + buffer[copyto] = buffer[index]; + } + copyto++; + index++; + } + if (index >= size) { + // didn't find terminating quote, something is seriously wrong + //throw new ArrayIndexOutOfBoundsException( + // "index = " + index + ", size = " + size); + return null; + } else + index++; // skip past the terminating quote + + if (returnString) + return toString(buffer, start, copyto); + else + return new ByteArray(buffer, start, copyto-start); + } else if (b == '{') { // Literal + int start = ++index; // note the start position + + while (buffer[index] != '}') + index++; + + int count = 0; + try { + count = ASCIIUtility.parseInt(buffer, start, index); + } catch (NumberFormatException nex) { + // throw new ParsingException(); + return null; + } + + start = index + 3; // skip "}\r\n" + index = start + count; // position index to beyond the literal + + if (returnString) // return as String + return toString(buffer, start, start + count); + else + return new ByteArray(buffer, start, count); + } else if (parseAtoms) { // parse as ASTRING-CHARs + int start = index; // track this, so that we can use to + // creating ByteArrayInputStream below. + String s = readDelimString(ASTRING_CHAR_DELIM); + if (returnString) + return s; + else // *very* unlikely + return new ByteArray(buffer, start, index); + } else if (b == 'N' || b == 'n') { // the only valid value is 'NIL' + index += 3; // skip past NIL + return null; + } + return null; // Error + } + + private String toString(byte[] buffer, int start, int end) { + return utf8 ? + new String(buffer, start, end - start, StandardCharsets.UTF_8) : + ASCIIUtility.toString(buffer, start, end); + } + + public int getType() { + return type; + } + + public boolean isContinuation() { + return ((type & TAG_MASK) == CONTINUATION); + } + + public boolean isTagged() { + return ((type & TAG_MASK) == TAGGED); + } + + public boolean isUnTagged() { + return ((type & TAG_MASK) == UNTAGGED); + } + + public boolean isOK() { + return ((type & TYPE_MASK) == OK); + } + + public boolean isNO() { + return ((type & TYPE_MASK) == NO); + } + + public boolean isBAD() { + return ((type & TYPE_MASK) == BAD); + } + + public boolean isBYE() { + return ((type & TYPE_MASK) == BYE); + } + + public boolean isSynthetic() { + return ((type & SYNTHETIC) == SYNTHETIC); + } + + /** + * Return the tag, if this is a tagged statement. + * + * @return tag of this tagged statement + */ + public String getTag() { + return tag; + } + + /** + * Return the rest of the response as a string, usually used to + * return the arbitrary message text after a NO response. + * + * @return the rest of the response + */ + public String getRest() { + skipSpaces(); + return toString(buffer, index, size); + } + + /** + * Return the exception for a synthetic BYE response. + * + * @return the exception + * @since JavaMail 1.5.4 + */ + public Exception getException() { + return ex; + } + + /** + * Reset pointer to beginning of response. + */ + public void reset() { + index = pindex; + } + + @Override + public String toString() { + return toString(buffer, 0, size); + } + +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/ResponseHandler.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/ResponseHandler.java new file mode 100644 index 000000000..54448cf25 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/ResponseHandler.java @@ -0,0 +1,51 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +/** + * This class + * + * @author John Mani + */ + +public interface ResponseHandler { + public void handleResponse(Response r); +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/ResponseInputStream.java b/fine-third-default/fine-mail/src/com/sun/mail/iap/ResponseInputStream.java new file mode 100644 index 000000000..8be4f36fa --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/ResponseInputStream.java @@ -0,0 +1,182 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.iap; + +import java.io.*; +import com.sun.mail.iap.ByteArray; +import com.sun.mail.util.ASCIIUtility; + +/** + * + * Inputstream that is used to read a Response. + * + * @author Arun Krishnan + * @author Bill Shannon + */ + +public class ResponseInputStream { + + private static final int minIncrement = 256; + private static final int maxIncrement = 256 * 1024; + private static final int incrementSlop = 16; + + // where we read from + private BufferedInputStream bin; + + /** + * Constructor. + * + * @param in the InputStream to wrap + */ + public ResponseInputStream(InputStream in) { + bin = new BufferedInputStream(in, 2 * 1024); + } + + /** + * Read a Response from the InputStream. + * + * @return ByteArray that contains the Response + * @exception IOException for I/O errors + */ + public ByteArray readResponse() throws IOException { + return readResponse(null); + } + + /** + * Read a Response from the InputStream. + * + * @param ba the ByteArray in which to store the response, or null + * @return ByteArray that contains the Response + * @exception IOException for I/O errors + */ + public ByteArray readResponse(ByteArray ba) throws IOException { + if (ba == null) + ba = new ByteArray(new byte[128], 0, 128); + + byte[] buffer = ba.getBytes(); + int idx = 0; + for (;;) { // read until CRLF with no preceeding literal + // XXX - b needs to be an int, to handle bytes with value 0xff + int b = 0; + boolean gotCRLF=false; + + // Read a CRLF terminated line from the InputStream + while (!gotCRLF && + ((b = bin.read()) != -1)) { + if (b == '\n') { + if ((idx > 0) && buffer[idx-1] == '\r') + gotCRLF = true; + } + if (idx >= buffer.length) { + int incr = buffer.length; + if (incr > maxIncrement) + incr = maxIncrement; + ba.grow(incr); + buffer = ba.getBytes(); + } + buffer[idx++] = (byte)b; + } + + if (b == -1) + throw new IOException("Connection dropped by server?"); + + // Now lets check for literals : {}CRLF + // Note: index needs to >= 5 for the above sequence to occur + if (idx < 5 || buffer[idx-3] != '}') + break; + + int i; + // look for left curly + for (i = idx - 4; i >= 0; i--) + if (buffer[i] == '{') + break; + + if (i < 0) // Nope, not a literal ? + break; + + int count = 0; + // OK, handle the literal .. + try { + count = ASCIIUtility.parseInt(buffer, i+1, idx-3); + } catch (NumberFormatException e) { + break; + } + + // Now read 'count' bytes. (Note: count could be 0) + if (count > 0) { + int avail = buffer.length - idx; // available space in buffer + if (count + incrementSlop > avail) { + // need count-avail more bytes + ba.grow(minIncrement > count + incrementSlop - avail ? + minIncrement : count + incrementSlop - avail); + buffer = ba.getBytes(); + } + + /* + * read() might not return all the bytes in one shot, + * so call repeatedly till we are done + */ + int actual; + while (count > 0) { + actual = bin.read(buffer, idx, count); + if (actual == -1) + throw new IOException("Connection dropped by server?"); + count -= actual; + idx += actual; + } + } + // back to top of loop to read until CRLF + } + ba.setCount(idx); + return ba; + } + + /** + * How much buffered data do we have? + * + * @return number of bytes available + * @exception IOException if the stream has been closed + * @since JavaMail 1.5.4 + */ + public int available() throws IOException { + return bin.available(); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/iap/package.html b/fine-third-default/fine-mail/src/com/sun/mail/iap/package.html new file mode 100644 index 000000000..6a2407cc2 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/iap/package.html @@ -0,0 +1,57 @@ + + + + + + +com.sun.mail.iap package + + + +

    +This package includes internal IMAP support classes and +SHOULD NOT BE USED DIRECTLY BY APPLICATIONS. +

    + + + diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/ACL.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/ACL.java new file mode 100644 index 000000000..5730b804e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/ACL.java @@ -0,0 +1,118 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.util.*; + +/** + * An access control list entry for a particular authentication identifier + * (user or group). Associates a set of Rights with the identifier. + * See RFC 2086. + *

    + * + * @author Bill Shannon + */ + +public class ACL implements Cloneable { + + private String name; + private Rights rights; + + /** + * Construct an ACL entry for the given identifier and with no rights. + * + * @param name the identifier name + */ + public ACL(String name) { + this.name = name; + this.rights = new Rights(); + } + + /** + * Construct an ACL entry for the given identifier with the given rights. + * + * @param name the identifier name + * @param rights the rights + */ + public ACL(String name, Rights rights) { + this.name = name; + this.rights = rights; + } + + /** + * Get the identifier name for this ACL entry. + * + * @return the identifier name + */ + public String getName() { + return name; + } + + /** + * Set the rights associated with this ACL entry. + * + * @param rights the rights + */ + public void setRights(Rights rights) { + this.rights = rights; + } + + /** + * Get the rights associated with this ACL entry. + * Returns the actual Rights object referenced by this ACL; + * modifications to the Rights object will effect this ACL. + * + * @return the rights + */ + public Rights getRights() { + return rights; + } + + /** + * Clone this ACL entry. + */ + @Override + public Object clone() throws CloneNotSupportedException { + ACL acl = (ACL)super.clone(); + acl.rights = (Rights)this.rights.clone(); + return acl; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/AppendUID.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/AppendUID.java new file mode 100644 index 000000000..78d46b2e1 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/AppendUID.java @@ -0,0 +1,61 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import com.sun.mail.iap.*; + +/** + * Information from the APPENDUID response code + * defined by the UIDPLUS extension - + * RFC 4315. + * + * @author Bill Shannon + */ + +public class AppendUID { + public long uidvalidity = -1; + public long uid = -1; + + public AppendUID(long uidvalidity, long uid) { + this.uidvalidity = uidvalidity; + this.uid = uid; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/CopyUID.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/CopyUID.java new file mode 100644 index 000000000..d4ac72333 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/CopyUID.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import com.sun.mail.imap.protocol.UIDSet; + +/** + * Information from the COPYUID response code + * defined by the UIDPLUS extension - + * RFC 4315. + * + * @author Bill Shannon + */ + +public class CopyUID { + public long uidvalidity = -1; + public UIDSet[] src; + public UIDSet[] dst; + + public CopyUID(long uidvalidity, UIDSet[] src, UIDSet[] dst) { + this.uidvalidity = uidvalidity; + this.src = src; + this.dst = dst; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/DefaultFolder.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/DefaultFolder.java new file mode 100644 index 000000000..2218ba47e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/DefaultFolder.java @@ -0,0 +1,151 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import javax.mail.Folder; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.MethodNotSupportedException; +import com.sun.mail.iap.ProtocolException; +import com.sun.mail.imap.protocol.IMAPProtocol; +import com.sun.mail.imap.protocol.ListInfo; + +/** + * The default IMAP folder (root of the naming hierarchy). + * + * @author John Mani + */ + +public class DefaultFolder extends IMAPFolder { + + protected DefaultFolder(IMAPStore store) { + super("", UNKNOWN_SEPARATOR, store, null); + exists = true; // of course + type = HOLDS_FOLDERS; // obviously + } + + @Override + public synchronized String getName() { + return fullName; + } + + @Override + public Folder getParent() { + return null; + } + + @Override + public synchronized Folder[] list(final String pattern) + throws MessagingException { + ListInfo[] li = null; + + li = (ListInfo[])doCommand(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) throws ProtocolException { + return p.list("", pattern); + } + }); + + if (li == null) + return new Folder[0]; + + IMAPFolder[] folders = new IMAPFolder[li.length]; + for (int i = 0; i < folders.length; i++) + folders[i] = ((IMAPStore)store).newIMAPFolder(li[i]); + return folders; + } + + @Override + public synchronized Folder[] listSubscribed(final String pattern) + throws MessagingException { + ListInfo[] li = null; + + li = (ListInfo[])doCommand(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) throws ProtocolException { + return p.lsub("", pattern); + } + }); + + if (li == null) + return new Folder[0]; + + IMAPFolder[] folders = new IMAPFolder[li.length]; + for (int i = 0; i < folders.length; i++) + folders[i] = ((IMAPStore)store).newIMAPFolder(li[i]); + return folders; + } + + @Override + public boolean hasNewMessages() throws MessagingException { + // Not applicable on DefaultFolder + return false; + } + + @Override + public Folder getFolder(String name) throws MessagingException { + return ((IMAPStore)store).newIMAPFolder(name, UNKNOWN_SEPARATOR); + } + + @Override + public boolean delete(boolean recurse) throws MessagingException { + // Not applicable on DefaultFolder + throw new MethodNotSupportedException("Cannot delete Default Folder"); + } + + @Override + public boolean renameTo(Folder f) throws MessagingException { + // Not applicable on DefaultFolder + throw new MethodNotSupportedException("Cannot rename Default Folder"); + } + + @Override + public void appendMessages(Message[] msgs) throws MessagingException { + // Not applicable on DefaultFolder + throw new MethodNotSupportedException("Cannot append to Default Folder"); + } + + @Override + public Message[] expunge() throws MessagingException { + // Not applicable on DefaultFolder + throw new MethodNotSupportedException("Cannot expunge Default Folder"); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPBodyPart.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPBodyPart.java new file mode 100644 index 000000000..416a0f4b4 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPBodyPart.java @@ -0,0 +1,478 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.io.*; + +import java.util.Enumeration; +import javax.mail.*; +import javax.mail.internet.*; +import javax.activation.*; + +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.ReadableMime; +import com.sun.mail.util.LineOutputStream; +import com.sun.mail.util.SharedByteArrayOutputStream; +import com.sun.mail.iap.*; +import com.sun.mail.imap.protocol.*; + +/** + * An IMAP body part. + * + * @author John Mani + * @author Bill Shannon + */ + +public class IMAPBodyPart extends MimeBodyPart implements ReadableMime { + private IMAPMessage message; + private BODYSTRUCTURE bs; + private String sectionId; + + // processed values .. + private String type; + private String description; + + private boolean headersLoaded = false; + + private static final boolean decodeFileName = + PropUtil.getBooleanSystemProperty("mail.mime.decodefilename", false); + + protected IMAPBodyPart(BODYSTRUCTURE bs, String sid, IMAPMessage message) { + super(); + this.bs = bs; + this.sectionId = sid; + this.message = message; + // generate content-type + ContentType ct = new ContentType(bs.type, bs.subtype, bs.cParams); + type = ct.toString(); + } + + /* Override this method to make it a no-op, rather than throw + * an IllegalWriteException. This will permit IMAPBodyParts to + * be inserted in newly crafted MimeMessages, especially when + * forwarding or replying to messages. + */ + @Override + protected void updateHeaders() { + return; + } + + @Override + public int getSize() throws MessagingException { + return bs.size; + } + + @Override + public int getLineCount() throws MessagingException { + return bs.lines; + } + + @Override + public String getContentType() throws MessagingException { + return type; + } + + @Override + public String getDisposition() throws MessagingException { + return bs.disposition; + } + + @Override + public void setDisposition(String disposition) throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + public String getEncoding() throws MessagingException { + return bs.encoding; + } + + @Override + public String getContentID() throws MessagingException { + return bs.id; + } + + @Override + public String getContentMD5() throws MessagingException { + return bs.md5; + } + + @Override + public void setContentMD5(String md5) throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + public String getDescription() throws MessagingException { + if (description != null) // cached value ? + return description; + + if (bs.description == null) + return null; + + try { + description = MimeUtility.decodeText(bs.description); + } catch (UnsupportedEncodingException ex) { + description = bs.description; + } + + return description; + } + + @Override + public void setDescription(String description, String charset) + throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + public String getFileName() throws MessagingException { + String filename = null; + if (bs.dParams != null) + filename = bs.dParams.get("filename"); + if ((filename == null || filename.isEmpty()) && bs.cParams != null) + filename = bs.cParams.get("name"); + if (decodeFileName && filename != null) { + try { + filename = MimeUtility.decodeText(filename); + } catch (UnsupportedEncodingException ex) { + throw new MessagingException("Can't decode filename", ex); + } + } + return filename; + } + + @Override + public void setFileName(String filename) throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + protected InputStream getContentStream() throws MessagingException { + InputStream is = null; + boolean pk = message.getPeek(); // acquire outside of message cache lock + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(message.getMessageCacheLock()) { + try { + IMAPProtocol p = message.getProtocol(); + + // Check whether this message is expunged + message.checkExpunged(); + + if (p.isREV1() && (message.getFetchBlockSize() != -1)) + return new IMAPInputStream(message, sectionId, + message.ignoreBodyStructureSize() ? -1 : bs.size, pk); + + // Else, vanila IMAP4, no partial fetch + + int seqnum = message.getSequenceNumber(); + BODY b; + if (pk) + b = p.peekBody(seqnum, sectionId); + else + b = p.fetchBody(seqnum, sectionId); + if (b != null) + is = b.getByteArrayInputStream(); + } catch (ConnectionException cex) { + throw new FolderClosedException( + message.getFolder(), cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + + if (is == null) { + message.forceCheckExpunged(); // may throw MessageRemovedException + // nope, the server doesn't think it's expunged. + // can't tell the difference between the server returning NIL + // and some other error that caused null to be returned above, + // so we'll just assume it was empty content. + is = new ByteArrayInputStream(new byte[0]); + } + return is; + } + + /** + * Return the MIME format stream of headers for this body part. + */ + private InputStream getHeaderStream() throws MessagingException { + if (!message.isREV1()) + loadHeaders(); // will be needed below + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(message.getMessageCacheLock()) { + try { + IMAPProtocol p = message.getProtocol(); + + // Check whether this message got expunged + message.checkExpunged(); + + if (p.isREV1()) { + int seqnum = message.getSequenceNumber(); + BODY b = p.peekBody(seqnum, sectionId + ".MIME"); + + if (b == null) + throw new MessagingException("Failed to fetch headers"); + + ByteArrayInputStream bis = b.getByteArrayInputStream(); + if (bis == null) + throw new MessagingException("Failed to fetch headers"); + return bis; + + } else { + // Can't read it from server, have to fake it + SharedByteArrayOutputStream bos = + new SharedByteArrayOutputStream(0); + LineOutputStream los = new LineOutputStream(bos); + + try { + // Write out the header + Enumeration hdrLines + = super.getAllHeaderLines(); + while (hdrLines.hasMoreElements()) + los.writeln(hdrLines.nextElement()); + + // The CRLF separator between header and content + los.writeln(); + } catch (IOException ioex) { + // should never happen + } finally { + try { + los.close(); + } catch (IOException cex) { } + } + return bos.toStream(); + } + } catch (ConnectionException cex) { + throw new FolderClosedException( + message.getFolder(), cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + } + + /** + * Return the MIME format stream corresponding to this message part. + * + * @return the MIME format stream + * @since JavaMail 1.4.5 + */ + @Override + public InputStream getMimeStream() throws MessagingException { + /* + * The IMAP protocol doesn't support returning the entire + * part content in one operation so we have to fake it by + * concatenating the header stream and the content stream. + */ + return new SequenceInputStream(getHeaderStream(), getContentStream()); + } + + @Override + public synchronized DataHandler getDataHandler() + throws MessagingException { + if (dh == null) { + if (bs.isMulti()) + dh = new DataHandler( + new IMAPMultipartDataSource( + this, bs.bodies, sectionId, message) + ); + else if (bs.isNested() && message.isREV1() && bs.envelope != null) + dh = new DataHandler( + new IMAPNestedMessage(message, + bs.bodies[0], + bs.envelope, + sectionId), + type + ); + } + + return super.getDataHandler(); + } + + @Override + public void setDataHandler(DataHandler content) throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + public void setContent(Object o, String type) throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + public void setContent(Multipart mp) throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + public String[] getHeader(String name) throws MessagingException { + loadHeaders(); + return super.getHeader(name); + } + + @Override + public void setHeader(String name, String value) + throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + public void addHeader(String name, String value) + throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + public void removeHeader(String name) throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + public Enumeration

    getAllHeaders() throws MessagingException { + loadHeaders(); + return super.getAllHeaders(); + } + + @Override + public Enumeration
    getMatchingHeaders(String[] names) + throws MessagingException { + loadHeaders(); + return super.getMatchingHeaders(names); + } + + @Override + public Enumeration
    getNonMatchingHeaders(String[] names) + throws MessagingException { + loadHeaders(); + return super.getNonMatchingHeaders(names); + } + + @Override + public void addHeaderLine(String line) throws MessagingException { + throw new IllegalWriteException("IMAPBodyPart is read-only"); + } + + @Override + public Enumeration getAllHeaderLines() throws MessagingException { + loadHeaders(); + return super.getAllHeaderLines(); + } + + @Override + public Enumeration getMatchingHeaderLines(String[] names) + throws MessagingException { + loadHeaders(); + return super.getMatchingHeaderLines(names); + } + + @Override + public Enumeration getNonMatchingHeaderLines(String[] names) + throws MessagingException { + loadHeaders(); + return super.getNonMatchingHeaderLines(names); + } + + private synchronized void loadHeaders() throws MessagingException { + if (headersLoaded) + return; + + // "headers" should never be null since it's set in the constructor. + // If something did go wrong this will fix it, but is an unsynchronized + // assignment of "headers". + if (headers == null) + headers = new InternetHeaders(); + + // load headers + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(message.getMessageCacheLock()) { + try { + IMAPProtocol p = message.getProtocol(); + + // Check whether this message got expunged + message.checkExpunged(); + + if (p.isREV1()) { + int seqnum = message.getSequenceNumber(); + BODY b = p.peekBody(seqnum, sectionId + ".MIME"); + + if (b == null) + throw new MessagingException("Failed to fetch headers"); + + ByteArrayInputStream bis = b.getByteArrayInputStream(); + if (bis == null) + throw new MessagingException("Failed to fetch headers"); + + headers.load(bis); + + } else { + + // RFC 1730 does not provide for fetching BodyPart headers + // So, just dump the RFC1730 BODYSTRUCTURE into the + // headerStore + + // Content-Type + headers.addHeader("Content-Type", type); + // Content-Transfer-Encoding + headers.addHeader("Content-Transfer-Encoding", bs.encoding); + // Content-Description + if (bs.description != null) + headers.addHeader("Content-Description", + bs.description); + // Content-ID + if (bs.id != null) + headers.addHeader("Content-ID", bs.id); + // Content-MD5 + if (bs.md5 != null) + headers.addHeader("Content-MD5", bs.md5); + } + } catch (ConnectionException cex) { + throw new FolderClosedException( + message.getFolder(), cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + headersLoaded = true; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPFolder.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPFolder.java new file mode 100644 index 000000000..058ad56f4 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPFolder.java @@ -0,0 +1,4177 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.util.Date; +import java.util.Vector; +import java.util.Hashtable; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Locale; +import java.util.logging.Level; +import java.io.*; +import java.net.SocketTimeoutException; +import java.nio.channels.SocketChannel; + +import javax.mail.*; +import javax.mail.event.*; +import javax.mail.internet.*; +import javax.mail.search.*; + +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.MailLogger; +import com.sun.mail.util.CRLFOutputStream; +import com.sun.mail.iap.*; +import com.sun.mail.imap.protocol.*; + +/** + * This class implements an IMAP folder.

    + * + * A closed IMAPFolder object shares a protocol connection with its IMAPStore + * object. When the folder is opened, it gets its own protocol connection.

    + * + * Applications that need to make use of IMAP-specific features may cast + * a Folder object to an IMAPFolder object and + * use the methods on this class.

    + * + * The {@link #getQuota getQuota} and + * {@link #setQuota setQuota} methods support the IMAP QUOTA extension. + * Refer to RFC 2087 + * for more information.

    + * + * The {@link #getACL getACL}, {@link #addACL addACL}, + * {@link #removeACL removeACL}, {@link #addRights addRights}, + * {@link #removeRights removeRights}, {@link #listRights listRights}, and + * {@link #myRights myRights} methods support the IMAP ACL extension. + * Refer to RFC 2086 + * for more information.

    + * + * The {@link #getSortedMessages getSortedMessages} + * methods support the IMAP SORT extension. + * Refer to RFC 5256 + * for more information.

    + * + * The {@link #open(int,com.sun.mail.imap.ResyncData) open(int,ResyncData)} + * method and {@link com.sun.mail.imap.ResyncData ResyncData} class supports + * the IMAP CONDSTORE and QRESYNC extensions. + * Refer to RFC 4551 + * and RFC 5162 + * for more information.

    + * + * The {@link #doCommand doCommand} method and + * {@link IMAPFolder.ProtocolCommand IMAPFolder.ProtocolCommand} + * interface support use of arbitrary IMAP protocol commands.

    + * + * See the com.sun.mail.imap package + * documentation for further information on the IMAP protocol provider.

    + * + * WARNING: The APIs unique to this class should be + * considered EXPERIMENTAL. They may be changed in the + * future in ways that are incompatible with applications using the + * current APIs. + * + * @author John Mani + * @author Bill Shannon + * @author Jim Glennon + */ + +/* + * The folder object itself serves as a lock for the folder's state + * EXCEPT for the message cache (see below), typically by using + * synchronized methods. When checking that a folder is open or + * closed, the folder's lock must be held. It's important that the + * folder's lock is acquired before the messageCacheLock (see below). + * Thus, the locking hierarchy is that the folder lock, while optional, + * must be acquired before the messageCacheLock, if it's acquired at + * all. Be especially careful of callbacks that occur while holding + * the messageCacheLock into (e.g.) superclass Folder methods that are + * synchronized. Note that methods in IMAPMessage will acquire the + * messageCacheLock without acquiring the folder lock.

    + * + * When a folder is opened, it creates a messageCache (a Vector) of + * empty IMAPMessage objects. Each Message has a messageNumber - which + * is its index into the messageCache, and a sequenceNumber - which is + * its IMAP sequence-number. All operations on a Message which involve + * communication with the server, use the message's sequenceNumber.

    + * + * The most important thing to note here is that the server can send + * unsolicited EXPUNGE notifications as part of the responses for "most" + * commands. Refer RFC 3501, sections 5.3 & 5.5 for gory details. Also, + * the server sends these notifications AFTER the message has been + * expunged. And once a message is expunged, the sequence-numbers of + * those messages after the expunged one are renumbered. This essentially + * means that the mapping between *any* Message and its sequence-number + * can change in the period when a IMAP command is issued and its responses + * are processed. Hence we impose a strict locking model as follows:

    + * + * We define one mutex per folder - this is just a Java Object (named + * messageCacheLock). Any time a command is to be issued to the IMAP + * server (i.e., anytime the corresponding IMAPProtocol method is + * invoked), follow the below style: + * + * synchronized (messageCacheLock) { // ACQUIRE LOCK + * issue command () + * + * // The response processing is typically done within + * // the handleResponse() callback. A few commands (Fetch, + * // Expunge) return *all* responses and hence their + * // processing is done here itself. Now, as part of the + * // processing unsolicited EXPUNGE responses, we renumber + * // the necessary sequence-numbers. Thus the renumbering + * // happens within this critical-region, surrounded by + * // locks. + * process responses () + * } // RELEASE LOCK + * + * This technique is used both by methods in IMAPFolder and by methods + * in IMAPMessage and other classes that operate on data in the folder. + * Note that holding the messageCacheLock has the side effect of + * preventing the folder from being closed, and thus ensuring that the + * folder's protocol object is still valid. The protocol object should + * only be accessed while holding the messageCacheLock (except for calls + * to IMAPProtocol.isREV1(), which don't need to be protected because it + * doesn't access the server). + * + * Note that interactions with the Store's protocol connection do + * not have to be protected as above, since the Store's protocol is + * never in a "meaningful" SELECT-ed state. + */ + +public class IMAPFolder extends Folder implements UIDFolder, ResponseHandler { + + protected volatile String fullName; // full name + protected String name; // name + protected int type; // folder type. + protected char separator; // separator + protected Flags availableFlags; // available flags + protected Flags permanentFlags; // permanent flags + protected volatile boolean exists; // whether this folder really exists ? + protected boolean isNamespace = false; // folder is a namespace name + protected volatile String[] attributes;// name attributes from LIST response + + protected volatile IMAPProtocol protocol; // this folder's protocol object + protected MessageCache messageCache;// message cache + // accessor lock for message cache + protected final Object messageCacheLock = new Object(); + + protected Hashtable uidTable; // UID->Message hashtable + + /* An IMAP delimiter is a 7bit US-ASCII character. (except NUL). + * We use '\uffff' (a non 7bit character) to indicate that we havent + * yet determined what the separator character is. + * We use '\u0000' (NUL) to indicate that no separator character + * exists, i.e., a flat hierarchy + */ + static final protected char UNKNOWN_SEPARATOR = '\uffff'; + + private volatile boolean opened = false; // is this folder opened ? + + /* This field tracks the state of this folder. If the folder is closed + * due to external causes (i.e, not thru the close() method), then + * this field will remain false. If the folder is closed thru the + * close() method, then this field is set to true. + * + * If reallyClosed is false, then a FolderClosedException is + * generated when a method is invoked on any Messaging object + * owned by this folder. If reallyClosed is true, then the + * IllegalStateException runtime exception is thrown. + */ + private boolean reallyClosed = true; + + /* + * The idleState field supports the IDLE command. + * Normally when executing an IMAP command we hold the + * messageCacheLock and often the folder lock (see above). + * While executing the IDLE command we can't hold either + * of these locks or it would prevent other threads from + * entering Folder methods even far enough to check whether + * an IDLE command is in progress. We need to check before + * issuing another command so that we can abort the IDLE + * command. + * + * The idleState field is protected by the messageCacheLock. + * The RUNNING state is the normal state and means no IDLE + * command is in progress. The IDLE state means we've issued + * an IDLE command and are reading responses. The ABORTING + * state means we've sent the DONE continuation command and + * are waiting for the thread running the IDLE command to + * break out of its read loop. + * + * When an IDLE command is in progress, the thread calling + * the idle method will be reading from the IMAP connection + * while holding neither the folder lock nor the messageCacheLock. + * It's obviously critical that no other thread try to send a + * command or read from the connection while in this state. + * However, other threads can send the DONE continuation + * command that will cause the server to break out of the IDLE + * loop and send the ending tag response to the IDLE command. + * The thread in the idle method that's reading the responses + * from the IDLE command will see this ending response and + * complete the idle method, setting the idleState field back + * to RUNNING, and notifying any threads waiting to use the + * connection. + * + * All uses of the IMAP connection (IMAPProtocol object) must + * be done while holding the messageCacheLock and must be + * preceeded by a check to make sure an IDLE command is not + * running, and abort the IDLE command if necessary. While + * waiting for the IDLE command to complete, these other threads + * will give up the messageCacheLock, but might still be holding + * the folder lock. This check is done by the getProtocol() + * method, resulting in a typical usage pattern of: + * + * synchronized (messageCacheLock) { + * IMAPProtocol p = getProtocol(); // may block waiting for IDLE + * // ... use protocol + * } + */ + private static final int RUNNING = 0; // not doing IDLE command + private static final int IDLE = 1; // IDLE command in effect + private static final int ABORTING = 2; // IDLE command aborting + private int idleState = RUNNING; + private IdleManager idleManager; + + private volatile int total = -1; // total number of messages in the + // message cache + private volatile int recent = -1; // number of recent messages + private int realTotal = -1; // total number of messages on + // the server + private long uidvalidity = -1; // UIDValidity + private long uidnext = -1; // UIDNext + private boolean uidNotSticky = false; // RFC 4315 + private volatile long highestmodseq = -1; // RFC 4551 - CONDSTORE + private boolean doExpungeNotification = true; // used in expunge handler + + private Status cachedStatus = null; + private long cachedStatusTime = 0; + + private boolean hasMessageCountListener = false; // optimize notification + + protected MailLogger logger; + private MailLogger connectionPoolLogger; + + /** + * A fetch profile item for fetching headers. + * This inner class extends the FetchProfile.Item + * class to add new FetchProfile item types, specific to IMAPFolders. + * + * @see FetchProfile + */ + public static class FetchProfileItem extends FetchProfile.Item { + protected FetchProfileItem(String name) { + super(name); + } + + /** + * HEADERS is a fetch profile item that can be included in a + * FetchProfile during a fetch request to a Folder. + * This item indicates that the headers for messages in the specified + * range are desired to be prefetched.

    + * + * An example of how a client uses this is below: + *

    +	 *
    +	 * 	FetchProfile fp = new FetchProfile();
    +	 *	fp.add(IMAPFolder.FetchProfileItem.HEADERS);
    +	 *	folder.fetch(msgs, fp);
    +	 *
    +	 * 
    + */ + public static final FetchProfileItem HEADERS = + new FetchProfileItem("HEADERS"); + + /** + * SIZE is a fetch profile item that can be included in a + * FetchProfile during a fetch request to a Folder. + * This item indicates that the sizes of the messages in the specified + * range are desired to be prefetched.

    + * + * SIZE was moved to FetchProfile.Item in JavaMail 1.5. + * + * @deprecated + */ + @Deprecated + public static final FetchProfileItem SIZE = + new FetchProfileItem("SIZE"); + + /** + * MESSAGE is a fetch profile item that can be included in a + * FetchProfile during a fetch request to a Folder. + * This item indicates that the entire messages (headers and body, + * including all "attachments") in the specified + * range are desired to be prefetched. Note that the entire message + * content is cached in memory while the Folder is open. The cached + * message will be parsed locally to return header information and + * message content.

    + * + * An example of how a client uses this is below: + *

    +	 *
    +	 * 	FetchProfile fp = new FetchProfile();
    +	 *	fp.add(IMAPFolder.FetchProfileItem.MESSAGE);
    +	 *	folder.fetch(msgs, fp);
    +	 *
    +	 * 
    + * + * @since JavaMail 1.5.2 + */ + public static final FetchProfileItem MESSAGE = + new FetchProfileItem("MESSAGE"); + + /** + * INTERNALDATE is a fetch profile item that can be included in a + * FetchProfile during a fetch request to a Folder. + * This item indicates that the IMAP INTERNALDATE values + * (received date) of the messages in the specified + * range are desired to be prefetched.

    + * + * An example of how a client uses this is below: + *

    +	 *
    +	 * 	FetchProfile fp = new FetchProfile();
    +	 *	fp.add(IMAPFolder.FetchProfileItem.INTERNALDATE);
    +	 *	folder.fetch(msgs, fp);
    +	 *
    +	 * 
    + * + * @since JavaMail 1.5.5 + */ + public static final FetchProfileItem INTERNALDATE = + new FetchProfileItem("INTERNALDATE"); + } + + /** + * Constructor used to create a possibly non-existent folder. + * + * @param fullName fullname of this folder + * @param separator the default separator character for this + * folder's namespace + * @param store the Store + * @param isNamespace if this folder represents a namespace + */ + protected IMAPFolder(String fullName, char separator, IMAPStore store, + Boolean isNamespace) { + super(store); + if (fullName == null) + throw new NullPointerException("Folder name is null"); + this.fullName = fullName; + this.separator = separator; + logger = new MailLogger(this.getClass(), "DEBUG IMAP", + store.getSession().getDebug(), store.getSession().getDebugOut()); + connectionPoolLogger = store.getConnectionPoolLogger(); + + /* + * Work around apparent bug in Exchange. Exchange + * will return a name of "Public Folders/" from + * LIST "%". + * + * If name has one separator, and it's at the end, + * assume this is a namespace name and treat it + * accordingly. Usually this will happen as a result + * of the list method, but this also allows getFolder + * to work with namespace names. + */ + this.isNamespace = false; + if (separator != UNKNOWN_SEPARATOR && separator != '\0') { + int i = this.fullName.indexOf(separator); + if (i > 0 && i == this.fullName.length() - 1) { + this.fullName = this.fullName.substring(0, i); + this.isNamespace = true; + } + } + + // if we were given a value, override default chosen above + if (isNamespace != null) + this.isNamespace = isNamespace.booleanValue(); + } + + /** + * Constructor used to create an existing folder. + * + * @param li the ListInfo for this folder + * @param store the store containing this folder + */ + protected IMAPFolder(ListInfo li, IMAPStore store) { + this(li.name, li.separator, store, null); + + if (li.hasInferiors) + type |= HOLDS_FOLDERS; + if (li.canOpen) + type |= HOLDS_MESSAGES; + exists = true; + attributes = li.attrs; + } + + /* + * Ensure that this folder exists. If 'exists' has been set to true, + * we don't attempt to validate it with the server again. Note that + * this can result in a possible loss of sync with the server. + * ASSERT: Must be called with this folder's synchronization lock held. + */ + protected void checkExists() throws MessagingException { + // If the boolean field 'exists' is false, check with the + // server by invoking exists() .. + if (!exists && !exists()) + throw new FolderNotFoundException( + this, fullName + " not found"); + } + + /* + * Ensure the folder is closed. + * ASSERT: Must be called with this folder's synchronization lock held. + */ + protected void checkClosed() { + if (opened) + throw new IllegalStateException( + "This operation is not allowed on an open folder" + ); + } + + /* + * Ensure the folder is open. + * ASSERT: Must be called with this folder's synchronization lock held. + */ + protected void checkOpened() throws FolderClosedException { + assert Thread.holdsLock(this); + if (!opened) { + if (reallyClosed) + throw new IllegalStateException( + "This operation is not allowed on a closed folder" + ); + else // Folder was closed "implicitly" + throw new FolderClosedException(this, + "Lost folder connection to server" + ); + } + } + + /* + * Check that the given message number is within the range + * of messages present in this folder. If the message + * number is out of range, we ping the server to obtain any + * pending new message notifications from the server. + */ + protected void checkRange(int msgno) throws MessagingException { + if (msgno < 1) // message-numbers start at 1 + throw new IndexOutOfBoundsException("message number < 1"); + + if (msgno <= total) + return; + + // Out of range, let's ping the server and see if + // the server has more messages for us. + + synchronized(messageCacheLock) { // Acquire lock + try { + keepConnectionAlive(false); + } catch (ConnectionException cex) { + // Oops, lost connection + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } // Release lock + + if (msgno > total) // Still out of range ? Throw up ... + throw new IndexOutOfBoundsException(msgno + " > " + total); + } + + /* + * Check whether the given flags are supported by this server, + * and also verify that the folder allows setting flags. + */ + private void checkFlags(Flags flags) throws MessagingException { + assert Thread.holdsLock(this); + if (mode != READ_WRITE) + throw new IllegalStateException( + "Cannot change flags on READ_ONLY folder: " + fullName + ); + /* + if (!availableFlags.contains(flags)) + throw new MessagingException( + "These flags are not supported by this implementation" + ); + */ + } + + /** + * Get the name of this folder. + */ + @Override + public synchronized String getName() { + /* Return the last component of this Folder's full name. + * Folder components are delimited by the separator character. + */ + if (name == null) { + try { + name = fullName.substring( + fullName.lastIndexOf(getSeparator()) + 1 + ); + } catch (MessagingException mex) { } + } + return name; + } + + /** + * Get the fullname of this folder. + */ + @Override + public String getFullName() { + return fullName; + } + + /** + * Get this folder's parent. + */ + @Override + public synchronized Folder getParent() throws MessagingException { + char c = getSeparator(); + int index; + if ((index = fullName.lastIndexOf(c)) != -1) + return ((IMAPStore)store).newIMAPFolder( + fullName.substring(0, index), c); + else + return new DefaultFolder((IMAPStore)store); + } + + /** + * Check whether this folder really exists on the server. + */ + @Override + public synchronized boolean exists() throws MessagingException { + // Check whether this folder exists .. + ListInfo[] li = null; + final String lname; + if (isNamespace && separator != '\0') + lname = fullName + separator; + else + lname = fullName; + + li = (ListInfo[])doCommand(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) throws ProtocolException { + return p.list("", lname); + } + }); + + if (li != null) { + int i = findName(li, lname); + fullName = li[i].name; + separator = li[i].separator; + int len = fullName.length(); + if (separator != '\0' && len > 0 && + fullName.charAt(len - 1) == separator) { + fullName = fullName.substring(0, len - 1); + } + type = 0; + if (li[i].hasInferiors) + type |= HOLDS_FOLDERS; + if (li[i].canOpen) + type |= HOLDS_MESSAGES; + exists = true; + attributes = li[i].attrs; + } else { + exists = opened; + attributes = null; + } + + return exists; + } + + /** + * Which entry in li matches lname? + * If the name contains wildcards, more than one entry may be + * returned. + */ + private int findName(ListInfo[] li, String lname) { + int i; + // if the name contains a wildcard, there might be more than one + for (i = 0; i < li.length; i++) { + if (li[i].name.equals(lname)) + break; + } + if (i >= li.length) { // nothing matched exactly + // XXX - possibly should fail? But what if server + // is case insensitive and returns the preferred + // case of the name here? + i = 0; // use first one + } + return i; + } + + /** + * List all subfolders matching the specified pattern. + */ + @Override + public Folder[] list(String pattern) throws MessagingException { + return doList(pattern, false); + } + + /** + * List all subscribed subfolders matching the specified pattern. + */ + @Override + public Folder[] listSubscribed(String pattern) throws MessagingException { + return doList(pattern, true); + } + + private synchronized Folder[] doList(final String pattern, + final boolean subscribed) throws MessagingException { + checkExists(); // insure that this folder does exist. + + // Why waste a roundtrip to the server? + if (attributes != null && !isDirectory()) + return new Folder[0]; + + final char c = getSeparator(); + + ListInfo[] li = (ListInfo[])doCommandIgnoreFailure( + new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + if (subscribed) + return p.lsub("", fullName + c + pattern); + else + return p.list("", fullName + c + pattern); + } + }); + + if (li == null) + return new Folder[0]; + + /* + * The UW based IMAP4 servers (e.g. SIMS2.0) include + * current folder (terminated with the separator), when + * the LIST pattern is '%' or '*'. i.e, + * returns "mail/" as the first LIST response. + * + * Doesn't make sense to include the current folder in this + * case, so we filter it out. Note that I'm assuming that + * the offending response is the *first* one, my experiments + * with the UW & SIMS2.0 servers indicate that .. + */ + int start = 0; + // Check the first LIST response. + if (li.length > 0 && li[0].name.equals(fullName + c)) + start = 1; // start from index = 1 + + IMAPFolder[] folders = new IMAPFolder[li.length - start]; + IMAPStore st = (IMAPStore)store; + for (int i = start; i < li.length; i++) + folders[i-start] = st.newIMAPFolder(li[i]); + return folders; + } + + /** + * Get the separator character. + */ + @Override + public synchronized char getSeparator() throws MessagingException { + if (separator == UNKNOWN_SEPARATOR) { + ListInfo[] li = null; + + li = (ListInfo[])doCommand(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + // REV1 allows the following LIST format to obtain + // the hierarchy delimiter of non-existent folders + if (p.isREV1()) // IMAP4rev1 + return p.list(fullName, ""); + else // IMAP4, note that this folder must exist for this + // to work :( + return p.list("", fullName); + } + }); + + if (li != null) + separator = li[0].separator; + else + separator = '/'; // punt ! + } + return separator; + } + + /** + * Get the type of this folder. + */ + @Override + public synchronized int getType() throws MessagingException { + if (opened) { + // never throw FolderNotFoundException if folder is open + if (attributes == null) + exists(); // try to fetch attributes + } else { + checkExists(); + } + return type; + } + + /** + * Check whether this folder is subscribed. + */ + @Override + public synchronized boolean isSubscribed() { + ListInfo[] li = null; + final String lname; + if (isNamespace && separator != '\0') + lname = fullName + separator; + else + lname = fullName; + + try { + li = (ListInfo[])doProtocolCommand(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + return p.lsub("", lname); + } + }); + } catch (ProtocolException pex) { + } + + if (li != null) { + int i = findName(li, lname); + return li[i].canOpen; + } else + return false; + } + + /** + * Subscribe/Unsubscribe this folder. + */ + @Override + public synchronized void setSubscribed(final boolean subscribe) + throws MessagingException { + doCommandIgnoreFailure(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) throws ProtocolException { + if (subscribe) + p.subscribe(fullName); + else + p.unsubscribe(fullName); + return null; + } + }); + } + + /** + * Create this folder, with the specified type. + */ + @Override + public synchronized boolean create(final int type) + throws MessagingException { + + char c = 0; + if ((type & HOLDS_MESSAGES) == 0) // only holds folders + c = getSeparator(); + final char sep = c; + Object ret = doCommandIgnoreFailure(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + if ((type & HOLDS_MESSAGES) == 0) // only holds folders + p.create(fullName + sep); + else { + p.create(fullName); + + // Certain IMAP servers do not allow creation of folders + // that can contain messages *and* subfolders. So, if we + // were asked to create such a folder, we should verify + // that we could indeed do so. + if ((type & HOLDS_FOLDERS) != 0) { + // we want to hold subfolders and messages. Check + // whether we could create such a folder. + ListInfo[] li = p.list("", fullName); + if (li != null && !li[0].hasInferiors) { + // Hmm ..the new folder + // doesn't support Inferiors ? Fail + p.delete(fullName); + throw new ProtocolException("Unsupported type"); + } + } + } + return Boolean.TRUE; + } + }); + + if (ret == null) + return false; // CREATE failure, maybe this + // folder already exists ? + + // exists = true; + // this.type = type; + boolean retb = exists(); // set exists, type, and attributes + if (retb) // Notify listeners on self and our Store + notifyFolderListeners(FolderEvent.CREATED); + return retb; + } + + /** + * Check whether this folder has new messages. + */ + @Override + public synchronized boolean hasNewMessages() throws MessagingException { + synchronized (messageCacheLock) { + if (opened) { // If we are open, we already have this information + // Folder is open, make sure information is up to date + // tickle the folder and store connections. + try { + keepConnectionAlive(true); + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + return recent > 0 ? true : false; + } + } + + // First, the cheap way - use LIST and look for the \Marked + // or \Unmarked tag + + ListInfo[] li = null; + final String lname; + if (isNamespace && separator != '\0') + lname = fullName + separator; + else + lname = fullName; + li = (ListInfo[])doCommandIgnoreFailure(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) throws ProtocolException { + return p.list("", lname); + } + }); + + // if folder doesn't exist, throw exception + if (li == null) + throw new FolderNotFoundException(this, fullName + " not found"); + + int i = findName(li, lname); + if (li[i].changeState == ListInfo.CHANGED) + return true; + else if (li[i].changeState == ListInfo.UNCHANGED) + return false; + + // LIST didn't work. Try the hard way, using STATUS + try { + Status status = getStatus(); + if (status.recent > 0) + return true; + else + return false; + } catch (BadCommandException bex) { + // Probably doesn't support STATUS, tough luck. + return false; + } catch (ConnectionException cex) { + throw new StoreClosedException(store, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + + /** + * Get the named subfolder. + */ + @Override + public synchronized Folder getFolder(String name) + throws MessagingException { + // If we know that this folder is *not* a directory, don't + // send the request to the server at all ... + if (attributes != null && !isDirectory()) + throw new MessagingException("Cannot contain subfolders"); + + char c = getSeparator(); + return ((IMAPStore)store).newIMAPFolder(fullName + c + name, c); + } + + /** + * Delete this folder. + */ + @Override + public synchronized boolean delete(boolean recurse) + throws MessagingException { + checkClosed(); // insure that this folder is closed. + + if (recurse) { + // Delete all subfolders. + Folder[] f = list(); + for (int i = 0; i < f.length; i++) + f[i].delete(recurse); // ignore intermediate failures + } + + // Attempt to delete this folder + + Object ret = doCommandIgnoreFailure(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) throws ProtocolException { + p.delete(fullName); + return Boolean.TRUE; + } + }); + + if (ret == null) + // Non-existent folder/No permission ?? + return false; + + // DELETE succeeded. + exists = false; + attributes = null; + + // Notify listeners on self and our Store + notifyFolderListeners(FolderEvent.DELETED); + return true; + } + + /** + * Rename this folder. + */ + @Override + public synchronized boolean renameTo(final Folder f) + throws MessagingException { + checkClosed(); // insure that we are closed. + checkExists(); + if (f.getStore() != store) + throw new MessagingException("Can't rename across Stores"); + + + Object ret = doCommandIgnoreFailure(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) throws ProtocolException { + p.rename(fullName, f.getFullName()); + return Boolean.TRUE; + } + }); + + if (ret == null) + return false; + + exists = false; + attributes = null; + notifyFolderRenamedListeners(f); + return true; + } + + /** + * Open this folder in the given mode. + */ + @Override + public synchronized void open(int mode) throws MessagingException { + open(mode, null); + } + + /** + * Open this folder in the given mode, with the given + * resynchronization data. + * + * @param mode the open mode (Folder.READ_WRITE or Folder.READ_ONLY) + * @param rd the ResyncData instance + * @return a List of MailEvent instances, or null if none + * @exception MessagingException if the open fails + * @since JavaMail 1.5.1 + */ + public synchronized List open(int mode, ResyncData rd) + throws MessagingException { + checkClosed(); // insure that we are not already open + + MailboxInfo mi = null; + // Request store for our own protocol connection. + protocol = ((IMAPStore)store).getProtocol(this); + + List openEvents = null; + synchronized(messageCacheLock) { // Acquire messageCacheLock + + /* + * Add response handler right away so we get any alerts or + * notifications that occur during the SELECT or EXAMINE. + * Have to be sure to remove it if we fail to open the + * folder. + */ + protocol.addResponseHandler(this); + + try { + /* + * Enable QRESYNC or CONDSTORE if needed and not enabled. + * QRESYNC implies CONDSTORE, but servers that support + * QRESYNC are not required to support just CONDSTORE + * per RFC 5162. + */ + if (rd != null) { + if (rd == ResyncData.CONDSTORE) { + if (!protocol.isEnabled("CONDSTORE") && + !protocol.isEnabled("QRESYNC")) { + if (protocol.hasCapability("CONDSTORE")) + protocol.enable("CONDSTORE"); + else + protocol.enable("QRESYNC"); + } + } else { + if (!protocol.isEnabled("QRESYNC")) + protocol.enable("QRESYNC"); + } + } + + if (mode == READ_ONLY) + mi = protocol.examine(fullName, rd); + else + mi = protocol.select(fullName, rd); + } catch (CommandFailedException cex) { + /* + * Handle SELECT or EXAMINE failure. + * Try to figure out why the operation failed so we can + * report a more reasonable exception. + * + * Will use our existing protocol object. + */ + try { + checkExists(); // throw exception if folder doesn't exist + + if ((type & HOLDS_MESSAGES) == 0) + throw new MessagingException( + "folder cannot contain messages"); + throw new MessagingException(cex.getMessage(), cex); + + } finally { + // folder not open, don't keep this information + exists = false; + attributes = null; + type = 0; + // connection still good, return it + releaseProtocol(true); + } + // NOTREACHED + } catch (ProtocolException pex) { + // got a BAD or a BYE; connection may be bad, close it + try { + throw logoutAndThrow(pex.getMessage(), pex); + } finally { + releaseProtocol(false); + } + } + + if (mi.mode != mode) { + if (mode == READ_WRITE && mi.mode == READ_ONLY && + ((IMAPStore)store).allowReadOnlySelect()) { + ; // all ok, allow it + } else { // otherwise, it's an error + ReadOnlyFolderException ife = new ReadOnlyFolderException( + this, "Cannot open in desired mode"); + throw cleanupAndThrow(ife); + } + } + + // Initialize stuff. + opened = true; + reallyClosed = false; + this.mode = mi.mode; + availableFlags = mi.availableFlags; + permanentFlags = mi.permanentFlags; + total = realTotal = mi.total; + recent = mi.recent; + uidvalidity = mi.uidvalidity; + uidnext = mi.uidnext; + uidNotSticky = mi.uidNotSticky; + highestmodseq = mi.highestmodseq; + + // Create the message cache of appropriate size + messageCache = new MessageCache(this, (IMAPStore)store, total); + + // process saved responses and return corresponding events + if (mi.responses != null) { + openEvents = new ArrayList<>(); + for (IMAPResponse ir : mi.responses) { + if (ir.keyEquals("VANISHED")) { + // "VANISHED" SP ["(EARLIER)"] SP known-uids + String[] s = ir.readAtomStringList(); + // check that it really is "EARLIER" + if (s == null || s.length != 1 || + !s[0].equalsIgnoreCase("EARLIER")) + continue; // it's not, what to do with it here? + String uids = ir.readAtom(); + UIDSet[] uidset = UIDSet.parseUIDSets(uids); + long[] luid = UIDSet.toArray(uidset, uidnext); + if (luid != null && luid.length > 0) + openEvents.add( + new MessageVanishedEvent(this, luid)); + } else if (ir.keyEquals("FETCH")) { + assert ir instanceof FetchResponse : + "!ir instanceof FetchResponse"; + Message msg = processFetchResponse((FetchResponse)ir); + if (msg != null) + openEvents.add(new MessageChangedEvent(this, + MessageChangedEvent.FLAGS_CHANGED, msg)); + } + } + } + } // Release lock + + exists = true; // if we opened it, it must exist + attributes = null; // but we don't yet know its attributes + type = HOLDS_MESSAGES; // lacking more info, we know at least this much + + // notify listeners + notifyConnectionListeners(ConnectionEvent.OPENED); + + return openEvents; + } + + private MessagingException cleanupAndThrow(MessagingException ife) { + try { + try { + // close mailbox and return connection + protocol.close(); + releaseProtocol(true); + } catch (ProtocolException pex) { + // something went wrong, close connection + try { + addSuppressed(ife, logoutAndThrow(pex.getMessage(), pex)); + } finally { + releaseProtocol(false); + } + } + } catch (Throwable thr) { + addSuppressed(ife, thr); + } + return ife; + } + + private MessagingException logoutAndThrow(String why, ProtocolException t) { + MessagingException ife = new MessagingException(why, t); + try { + protocol.logout(); + } catch (Throwable thr) { + addSuppressed(ife, thr); + } + return ife; + } + + private void addSuppressed(Throwable ife, Throwable thr) { + if (isRecoverable(thr)) { + ife.addSuppressed(thr); + } else { + thr.addSuppressed(ife); + if (thr instanceof Error) { + throw (Error) thr; + } + if (thr instanceof RuntimeException) { + throw (RuntimeException) thr; + } + throw new RuntimeException("unexpected exception", thr); + } + } + + private boolean isRecoverable(Throwable t) { + return (t instanceof Exception) || (t instanceof LinkageError); + } + + /** + * Prefetch attributes, based on the given FetchProfile. + */ + @Override + public synchronized void fetch(Message[] msgs, FetchProfile fp) + throws MessagingException { + // cache this information in case connection is closed and + // protocol is set to null + boolean isRev1; + FetchItem[] fitems; + synchronized (messageCacheLock) { + checkOpened(); + isRev1 = protocol.isREV1(); + fitems = protocol.getFetchItems(); + } + + StringBuilder command = new StringBuilder(); + boolean first = true; + boolean allHeaders = false; + + if (fp.contains(FetchProfile.Item.ENVELOPE)) { + command.append(getEnvelopeCommand()); + first = false; + } + if (fp.contains(FetchProfile.Item.FLAGS)) { + command.append(first ? "FLAGS" : " FLAGS"); + first = false; + } + if (fp.contains(FetchProfile.Item.CONTENT_INFO)) { + command.append(first ? "BODYSTRUCTURE" : " BODYSTRUCTURE"); + first = false; + } + if (fp.contains(UIDFolder.FetchProfileItem.UID)) { + command.append(first ? "UID" : " UID"); + first = false; + } + if (fp.contains(IMAPFolder.FetchProfileItem.HEADERS)) { + allHeaders = true; + if (isRev1) + command.append(first ? + "BODY.PEEK[HEADER]" : " BODY.PEEK[HEADER]"); + else + command.append(first ? "RFC822.HEADER" : " RFC822.HEADER"); + first = false; + } + if (fp.contains(IMAPFolder.FetchProfileItem.MESSAGE)) { + allHeaders = true; + if (isRev1) + command.append(first ? "BODY.PEEK[]" : " BODY.PEEK[]"); + else + command.append(first ? "RFC822" : " RFC822"); + first = false; + } + if (fp.contains(FetchProfile.Item.SIZE) || + fp.contains(IMAPFolder.FetchProfileItem.SIZE)) { + command.append(first ? "RFC822.SIZE" : " RFC822.SIZE"); + first = false; + } + if (fp.contains(IMAPFolder.FetchProfileItem.INTERNALDATE)) { + command.append(first ? "INTERNALDATE" : " INTERNALDATE"); + first = false; + } + + // if we're not fetching all headers, fetch individual headers + String[] hdrs = null; + if (!allHeaders) { + hdrs = fp.getHeaderNames(); + if (hdrs.length > 0) { + if (!first) + command.append(" "); + command.append(createHeaderCommand(hdrs, isRev1)); + } + } + + /* + * Add any additional extension fetch items. + */ + for (int i = 0; i < fitems.length; i++) { + if (fp.contains(fitems[i].getFetchProfileItem())) { + if (command.length() != 0) + command.append(" "); + command.append(fitems[i].getName()); + } + } + + Utility.Condition condition = + new IMAPMessage.FetchProfileCondition(fp, fitems); + + // Acquire the Folder's MessageCacheLock. + synchronized (messageCacheLock) { + + // check again to make sure folder is still open + checkOpened(); + + // Apply the test, and get the sequence-number set for + // the messages that need to be prefetched. + MessageSet[] msgsets = Utility.toMessageSetSorted(msgs, condition); + + if (msgsets == null) + // We already have what we need. + return; + + Response[] r = null; + // to collect non-FETCH responses & unsolicited FETCH FLAG responses + List v = new ArrayList<>(); + try { + r = getProtocol().fetch(msgsets, command.toString()); + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (CommandFailedException cfx) { + // Ignore these, as per RFC 2180 + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + + if (r == null) + return; + + for (int i = 0; i < r.length; i++) { + if (r[i] == null) + continue; + if (!(r[i] instanceof FetchResponse)) { + v.add(r[i]); // Unsolicited Non-FETCH response + continue; + } + + // Got a FetchResponse. + FetchResponse f = (FetchResponse)r[i]; + // Get the corresponding message. + IMAPMessage msg = getMessageBySeqNumber(f.getNumber()); + + int count = f.getItemCount(); + boolean unsolicitedFlags = false; + + for (int j = 0; j < count; j++) { + Item item = f.getItem(j); + // Check for the FLAGS item + if (item instanceof Flags && + (!fp.contains(FetchProfile.Item.FLAGS) || + msg == null)) { + // Ok, Unsolicited FLAGS update. + unsolicitedFlags = true; + } else if (msg != null) + msg.handleFetchItem(item, hdrs, allHeaders); + } + if (msg != null) + msg.handleExtensionFetchItems(f.getExtensionItems()); + + // If this response contains any unsolicited FLAGS + // add it to the unsolicited response vector + if (unsolicitedFlags) + v.add(f); + } + + // Dispatch any unsolicited responses + if (!v.isEmpty()) { + Response[] responses = new Response[v.size()]; + v.toArray(responses); + handleResponses(responses); + } + + } // Release messageCacheLock + } + + /** + * Return the IMAP FETCH items to request in order to load + * all the "envelope" data. Subclasses can override this + * method to fetch more data when FetchProfile.Item.ENVELOPE + * is requested. + * + * @return the IMAP FETCH items to request + * @since JavaMail 1.4.6 + */ + protected String getEnvelopeCommand() { + return IMAPMessage.EnvelopeCmd; + } + + /** + * Create a new IMAPMessage object to represent the given message number. + * Subclasses of IMAPFolder may override this method to create a + * subclass of IMAPMessage. + * + * @param msgnum the message sequence number + * @return the new IMAPMessage object + * @since JavaMail 1.4.6 + */ + protected IMAPMessage newIMAPMessage(int msgnum) { + return new IMAPMessage(this, msgnum); + } + + /** + * Create the appropriate IMAP FETCH command items to fetch the + * requested headers. + */ + private String createHeaderCommand(String[] hdrs, boolean isRev1) { + StringBuilder sb; + + if (isRev1) + sb = new StringBuilder("BODY.PEEK[HEADER.FIELDS ("); + else + sb = new StringBuilder("RFC822.HEADER.LINES ("); + + for (int i = 0; i < hdrs.length; i++) { + if (i > 0) + sb.append(" "); + sb.append(hdrs[i]); + } + + if (isRev1) + sb.append(")]"); + else + sb.append(")"); + + return sb.toString(); + } + + /** + * Set the specified flags for the given array of messages. + */ + @Override + public synchronized void setFlags(Message[] msgs, Flags flag, boolean value) + throws MessagingException { + checkOpened(); + checkFlags(flag); // validate flags + + if (msgs.length == 0) // boundary condition + return; + + synchronized(messageCacheLock) { + try { + IMAPProtocol p = getProtocol(); + MessageSet[] ms = Utility.toMessageSetSorted(msgs, null); + if (ms == null) + throw new MessageRemovedException( + "Messages have been removed"); + p.storeFlags(ms, flag, value); + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + } + + /** + * Set the specified flags for the given range of message numbers. + */ + @Override + public synchronized void setFlags(int start, int end, + Flags flag, boolean value) throws MessagingException { + checkOpened(); + Message[] msgs = new Message[end - start + 1]; + int i = 0; + for (int n = start; n <= end; n++) + msgs[i++] = getMessage(n); + setFlags(msgs, flag, value); + } + + /** + * Set the specified flags for the given array of message numbers. + */ + @Override + public synchronized void setFlags(int[] msgnums, Flags flag, boolean value) + throws MessagingException { + checkOpened(); + Message[] msgs = new Message[msgnums.length]; + for (int i = 0; i < msgnums.length; i++) + msgs[i] = getMessage(msgnums[i]); + setFlags(msgs, flag, value); + } + + /** + * Close this folder. + */ + @Override + public synchronized void close(boolean expunge) throws MessagingException { + close(expunge, false); + } + + /** + * Close this folder without waiting for the server. + * + * @exception MessagingException for failures + */ + public synchronized void forceClose() throws MessagingException { + close(false, true); + } + + /* + * Common close method. + */ + private void close(boolean expunge, boolean force) + throws MessagingException { + assert Thread.holdsLock(this); + synchronized(messageCacheLock) { + /* + * If we already know we're closed, this is illegal. + * Can't use checkOpened() because if we were forcibly + * closed asynchronously we just want to complete the + * closing here. + */ + if (!opened && reallyClosed) + throw new IllegalStateException( + "This operation is not allowed on a closed folder" + ); + + reallyClosed = true; // Ok, lets reset + + // Maybe this folder is already closed, or maybe another + // thread which had the messageCacheLock earlier, found + // that our server connection is dead and cleaned up + // everything .. + if (!opened) + return; + + boolean reuseProtocol = true; + try { + waitIfIdle(); + if (force) { + logger.log(Level.FINE, "forcing folder {0} to close", + fullName); + if (protocol != null) + protocol.disconnect(); + } else if (((IMAPStore)store).isConnectionPoolFull()) { + // If the connection pool is full, logout the connection + logger.fine( + "pool is full, not adding an Authenticated connection"); + + // If the expunge flag is set, close the folder first. + if (expunge && protocol != null) + protocol.close(); + + if (protocol != null) + protocol.logout(); + } else { + // If the expunge flag is set or we're open read-only we + // can just close the folder, otherwise open it read-only + // before closing, or unselect it if supported. + if (!expunge && mode == READ_WRITE) { + try { + if (protocol != null && + protocol.hasCapability("UNSELECT")) + protocol.unselect(); + else { + // Unselect isn't supported so we need to + // select a folder to cause this one to be + // deselected without expunging messages. + // We try to do that by reopening the current + // folder read-only. If the current folder + // was renamed out from under us, the EXAMINE + // might fail, but that's ok because it still + // leaves us with the folder deselected. + if (protocol != null) { + boolean selected = true; + try { + protocol.examine(fullName); + // success, folder still selected + } catch (CommandFailedException ex) { + // EXAMINE failed, folder is no + // longer selected + selected = false; + } + if (selected && protocol != null) + protocol.close(); + } + } + } catch (ProtocolException pex2) { + reuseProtocol = false; // something went wrong + } + } else { + if (protocol != null) + protocol.close(); + } + } + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + // cleanup if we haven't already + if (opened) + cleanup(reuseProtocol); + } + } + } + + // NOTE: this method can currently be invoked from close() or + // from handleResponses(). Both invocations are conditional, + // based on the "opened" flag, so we are sure that multiple + // Connection.CLOSED events are not generated. Also both + // invocations are from within messageCacheLock-ed areas. + private void cleanup(boolean returnToPool) { + assert Thread.holdsLock(messageCacheLock); + releaseProtocol(returnToPool); + messageCache = null; + uidTable = null; + exists = false; // to force a recheck in exists(). + attributes = null; + opened = false; + idleState = RUNNING; // just in case + messageCacheLock.notifyAll(); // wake up anyone waiting + notifyConnectionListeners(ConnectionEvent.CLOSED); + } + + /** + * Check whether this connection is really open. + */ + @Override + public synchronized boolean isOpen() { + synchronized(messageCacheLock) { + // Probe the connection to make sure its really open. + if (opened) { + try { + keepConnectionAlive(false); + } catch (ProtocolException pex) { } + } + } + + return opened; + } + + /** + * Return the permanent flags supported by the server. + */ + @Override + public synchronized Flags getPermanentFlags() { + if (permanentFlags == null) + return null; + return (Flags)(permanentFlags.clone()); + } + + /** + * Get the total message count. + */ + @Override + public synchronized int getMessageCount() throws MessagingException { + synchronized (messageCacheLock) { + if (opened) { + // Folder is open, we know what the total message count is .. + // tickle the folder and store connections. + try { + keepConnectionAlive(true); + return total; + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + } + + // If this folder is not yet open, we use STATUS to + // get the total message count + checkExists(); + try { + Status status = getStatus(); + return status.total; + } catch (BadCommandException bex) { + // doesn't support STATUS, probably vanilla IMAP4 .. + // lets try EXAMINE + IMAPProtocol p = null; + + try { + p = getStoreProtocol(); // XXX + MailboxInfo minfo = p.examine(fullName); + p.close(); + return minfo.total; + } catch (ProtocolException pex) { + // Give up. + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + } catch (ConnectionException cex) { + throw new StoreClosedException(store, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + + /** + * Get the new message count. + */ + @Override + public synchronized int getNewMessageCount() throws MessagingException { + synchronized (messageCacheLock) { + if (opened) { + // Folder is open, we know what the new message count is .. + // tickle the folder and store connections. + try { + keepConnectionAlive(true); + return recent; + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + } + + // If this folder is not yet open, we use STATUS to + // get the new message count + checkExists(); + try { + Status status = getStatus(); + return status.recent; + } catch (BadCommandException bex) { + // doesn't support STATUS, probably vanilla IMAP4 .. + // lets try EXAMINE + IMAPProtocol p = null; + + try { + p = getStoreProtocol(); // XXX + MailboxInfo minfo = p.examine(fullName); + p.close(); + return minfo.recent; + } catch (ProtocolException pex) { + // Give up. + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + } catch (ConnectionException cex) { + throw new StoreClosedException(store, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + + /** + * Get the unread message count. + */ + @Override + public synchronized int getUnreadMessageCount() + throws MessagingException { + if (!opened) { + checkExists(); + // If this folder is not yet open, we use STATUS to + // get the unseen message count + try { + Status status = getStatus(); + return status.unseen; + } catch (BadCommandException bex) { + // doesn't support STATUS, probably vanilla IMAP4 .. + // Could EXAMINE, SEARCH for UNREAD messages and + // return the count .. bah, not worth it. + return -1; + } catch (ConnectionException cex) { + throw new StoreClosedException(store, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + + // if opened, issue server-side search for messages that do + // *not* have the SEEN flag. + Flags f = new Flags(); + f.add(Flags.Flag.SEEN); + try { + synchronized(messageCacheLock) { + int[] matches = getProtocol().search(new FlagTerm(f, false)); + return matches.length; // NOTE: 'matches' is never null + } + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + // Shouldn't happen + throw new MessagingException(pex.getMessage(), pex); + } + } + + /** + * Get the deleted message count. + */ + @Override + public synchronized int getDeletedMessageCount() + throws MessagingException { + if (!opened) { + checkExists(); + // no way to do this on closed folders + return -1; + } + + // if opened, issue server-side search for messages that do + // have the DELETED flag. + Flags f = new Flags(); + f.add(Flags.Flag.DELETED); + try { + synchronized(messageCacheLock) { + int[] matches = getProtocol().search(new FlagTerm(f, true)); + return matches.length; // NOTE: 'matches' is never null + } + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + // Shouldn't happen + throw new MessagingException(pex.getMessage(), pex); + } + } + + /* + * Get results of STATUS command for this folder, checking cache first. + * ASSERT: Must be called with this folder's synchronization lock held. + * ASSERT: The folder must be closed. + */ + private Status getStatus() throws ProtocolException { + int statusCacheTimeout = ((IMAPStore)store).getStatusCacheTimeout(); + + // if allowed to cache and our cache is still valid, return it + if (statusCacheTimeout > 0 && cachedStatus != null && + System.currentTimeMillis() - cachedStatusTime < statusCacheTimeout) + return cachedStatus; + + IMAPProtocol p = null; + + try { + p = getStoreProtocol(); // XXX + Status s = p.status(fullName, null); + // if allowed to cache, do so + if (statusCacheTimeout > 0) { + cachedStatus = s; + cachedStatusTime = System.currentTimeMillis(); + } + return s; + } finally { + releaseStoreProtocol(p); + } + } + + /** + * Get the specified message. + */ + @Override + public synchronized Message getMessage(int msgnum) + throws MessagingException { + checkOpened(); + checkRange(msgnum); + + return messageCache.getMessage(msgnum); + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized Message[] getMessages() throws MessagingException { + /* + * Need to override Folder method to throw FolderClosedException + * instead of IllegalStateException if not really closed. + */ + checkOpened(); + int total = getMessageCount(); + Message[] msgs = new Message[total]; + for (int i = 1; i <= total; i++) + msgs[i - 1] = messageCache.getMessage(i); + return msgs; + } + + /** + * Append the given messages into this folder. + */ + @Override + public synchronized void appendMessages(Message[] msgs) + throws MessagingException { + checkExists(); // verify that self exists + + // XXX - have to verify that messages are in a different + // store (if any) than target folder, otherwise could + // deadlock trying to fetch messages on the same connection + // we're using for the append. + + int maxsize = ((IMAPStore)store).getAppendBufferSize(); + + for (int i = 0; i < msgs.length; i++) { + final Message m = msgs[i]; + Date d = m.getReceivedDate(); // retain dates + if (d == null) + d = m.getSentDate(); + final Date dd = d; + final Flags f = m.getFlags(); + + final MessageLiteral mos; + try { + // if we know the message is too big, don't buffer any of it + mos = new MessageLiteral(m, + m.getSize() > maxsize ? 0 : maxsize); + } catch (IOException ex) { + throw new MessagingException( + "IOException while appending messages", ex); + } catch (MessageRemovedException mrex) { + continue; // just skip this expunged message + } + + doCommand(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + p.append(fullName, f, dd, mos); + return null; + } + }); + } + } + + /** + * Append the given messages into this folder. + * Return array of AppendUID objects containing + * UIDs of these messages in the destination folder. + * Each element of the returned array corresponds to + * an element of the msgs array. A null + * element means the server didn't return UID information + * for the appended message.

    + * + * Depends on the APPENDUID response code defined by the + * UIDPLUS extension - + * RFC 4315. + * + * @param msgs the messages to append + * @return array of AppendUID objects + * @exception MessagingException for failures + * @since JavaMail 1.4 + */ + public synchronized AppendUID[] appendUIDMessages(Message[] msgs) + throws MessagingException { + checkExists(); // verify that self exists + + // XXX - have to verify that messages are in a different + // store (if any) than target folder, otherwise could + // deadlock trying to fetch messages on the same connection + // we're using for the append. + + int maxsize = ((IMAPStore)store).getAppendBufferSize(); + + AppendUID[] uids = new AppendUID[msgs.length]; + for (int i = 0; i < msgs.length; i++) { + final Message m = msgs[i]; + final MessageLiteral mos; + + try { + // if we know the message is too big, don't buffer any of it + mos = new MessageLiteral(m, + m.getSize() > maxsize ? 0 : maxsize); + } catch (IOException ex) { + throw new MessagingException( + "IOException while appending messages", ex); + } catch (MessageRemovedException mrex) { + continue; // just skip this expunged message + } + + Date d = m.getReceivedDate(); // retain dates + if (d == null) + d = m.getSentDate(); + final Date dd = d; + final Flags f = m.getFlags(); + AppendUID auid = (AppendUID)doCommand(new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + return p.appenduid(fullName, f, dd, mos); + } + }); + uids[i] = auid; + } + return uids; + } + + /** + * Append the given messages into this folder. + * Return array of Message objects representing + * the messages in the destination folder. Note + * that the folder must be open. + * Each element of the returned array corresponds to + * an element of the msgs array. A null + * element means the server didn't return UID information + * for the appended message.

    + * + * Depends on the APPENDUID response code defined by the + * UIDPLUS extension - + * RFC 4315. + * + * @param msgs the messages to add + * @return the messages in this folder + * @exception MessagingException for failures + * @since JavaMail 1.4 + */ + public synchronized Message[] addMessages(Message[] msgs) + throws MessagingException { + checkOpened(); + Message[] rmsgs = new MimeMessage[msgs.length]; + AppendUID[] uids = appendUIDMessages(msgs); + for (int i = 0; i < uids.length; i++) { + AppendUID auid = uids[i]; + if (auid != null) { + if (auid.uidvalidity == uidvalidity) { + try { + rmsgs[i] = getMessageByUID(auid.uid); + } catch (MessagingException mex) { + // ignore errors at this stage + } + } + } + } + return rmsgs; + } + + /** + * Copy the specified messages from this folder, to the + * specified destination. + */ + @Override + public synchronized void copyMessages(Message[] msgs, Folder folder) + throws MessagingException { + copymoveMessages(msgs, folder, false); + } + + /** + * Copy the specified messages from this folder, to the + * specified destination. + * Return array of AppendUID objects containing + * UIDs of these messages in the destination folder. + * Each element of the returned array corresponds to + * an element of the msgs array. A null + * element means the server didn't return UID information + * for the copied message.

    + * + * Depends on the COPYUID response code defined by the + * UIDPLUS extension - + * RFC 4315. + * + * @param msgs the messages to copy + * @param folder the folder to copy the messages to + * @return array of AppendUID objects + * @exception MessagingException for failures + * @since JavaMail 1.5.1 + */ + public synchronized AppendUID[] copyUIDMessages(Message[] msgs, + Folder folder) throws MessagingException { + return copymoveUIDMessages(msgs, folder, false); + } + + /** + * Move the specified messages from this folder, to the + * specified destination. + * + * Depends on the MOVE extension + * (RFC 6851). + * + * @param msgs the messages to move + * @param folder the folder to move the messages to + * @exception MessagingException for failures + * + * @since JavaMail 1.5.4 + */ + public synchronized void moveMessages(Message[] msgs, Folder folder) + throws MessagingException { + copymoveMessages(msgs, folder, true); + } + + /** + * Move the specified messages from this folder, to the + * specified destination. + * Return array of AppendUID objects containing + * UIDs of these messages in the destination folder. + * Each element of the returned array corresponds to + * an element of the msgs array. A null + * element means the server didn't return UID information + * for the moved message.

    + * + * Depends on the MOVE extension + * (RFC 6851) + * and the COPYUID response code defined by the + * UIDPLUS extension + * (RFC 4315). + * + * @param msgs the messages to move + * @param folder the folder to move the messages to + * @return array of AppendUID objects + * @exception MessagingException for failures + * @since JavaMail 1.5.4 + */ + public synchronized AppendUID[] moveUIDMessages(Message[] msgs, + Folder folder) throws MessagingException { + return copymoveUIDMessages(msgs, folder, true); + } + + /** + * Copy or move the specified messages from this folder, to the + * specified destination. + * + * @since JavaMail 1.5.4 + */ + private synchronized void copymoveMessages(Message[] msgs, Folder folder, + boolean move) throws MessagingException { + checkOpened(); + + if (msgs.length == 0) // boundary condition + return; + + // If the destination belongs to our same store, optimize + if (folder.getStore() == store) { + synchronized(messageCacheLock) { + try { + IMAPProtocol p = getProtocol(); + MessageSet[] ms = Utility.toMessageSet(msgs, null); + if (ms == null) + throw new MessageRemovedException( + "Messages have been removed"); + if (move) + p.move(ms, folder.getFullName()); + else + p.copy(ms, folder.getFullName()); + } catch (CommandFailedException cfx) { + if (cfx.getMessage().indexOf("TRYCREATE") != -1) + throw new FolderNotFoundException( + folder, + folder.getFullName() + " does not exist" + ); + else + throw new MessagingException(cfx.getMessage(), cfx); + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + } else // destination is a different store. + if (move) + throw new MessagingException( + "Move between stores not supported"); + else + super.copyMessages(msgs, folder); + } + + /** + * Copy or move the specified messages from this folder, to the + * specified destination. + * Return array of AppendUID objects containing + * UIDs of these messages in the destination folder. + * Each element of the returned array corresponds to + * an element of the msgs array. A null + * element means the server didn't return UID information + * for the copied message.

    + * + * Depends on the COPYUID response code defined by the + * UIDPLUS extension - + * RFC 4315. + * Move depends on the MOVE extension - + * RFC 6851. + * + * @param msgs the messages to copy + * @param folder the folder to copy the messages to + * @param move move instead of copy? + * @return array of AppendUID objects + * @exception MessagingException for failures + * @since JavaMail 1.5.4 + */ + private synchronized AppendUID[] copymoveUIDMessages(Message[] msgs, + Folder folder, boolean move) throws MessagingException { + checkOpened(); + + if (msgs.length == 0) // boundary condition + return null; + + // the destination must belong to our same store + if (folder.getStore() != store) // destination is a different store. + throw new MessagingException( + move ? + "can't moveUIDMessages to a different store" : + "can't copyUIDMessages to a different store"); + + // call fetch to make sure we have all the UIDs + // necessary to interpret the COPYUID response + FetchProfile fp = new FetchProfile(); + fp.add(UIDFolder.FetchProfileItem.UID); + fetch(msgs, fp); + // XXX - could pipeline the FETCH with the COPY/MOVE below + + synchronized (messageCacheLock) { + try { + IMAPProtocol p = getProtocol(); + // XXX - messages have to be from this Folder, who checks? + MessageSet[] ms = Utility.toMessageSet(msgs, null); + if (ms == null) + throw new MessageRemovedException( + "Messages have been removed"); + CopyUID cuid; + if (move) + cuid = p.moveuid(ms, folder.getFullName()); + else + cuid = p.copyuid(ms, folder.getFullName()); + + /* + * Correlate source UIDs with destination UIDs. + * This won't be time or space efficient if there's + * a lot of messages. + * + * In order to make sense of the returned UIDs, we need + * the UIDs for every one of the original messages. + * We fetch them above, to make sure we have them. + * This is critical for MOVE since after the MOVE the + * messages are gone/expunged. + * + * Assume the common case is that the messages are + * in order by UID. Map the returned source + * UIDs to their corresponding Message objects. + * Step through the msgs array looking for the + * Message object in the returned source message + * list. Most commonly the source message (UID) + * for the Nth original message will be in the Nth + * position in the returned source message (UID) + * list. Thus, the destination UID is in the Nth + * position in the returned destination UID list. + * But if the source message isn't where expected, + * we have to search the entire source message + * list, starting from where we expect it and + * wrapping around until we've searched it all. + * (Gmail will often return the lists in an unexpected order.) + * + * A possible optimization: + * If the number of UIDs returned is the same as the + * number of messages being copied/moved, we could + * sort the source messages by message number, sort + * the source and destination parallel arrays by source + * UID, and the resulting message and destination UID + * arrays will correspond. + * + * If the returned UID array size is different, some + * message was expunged while we were trying to copy/move it. + * This should be rare but would mean falling back to the + * general algorithm. + */ + long[] srcuids = UIDSet.toArray(cuid.src); + long[] dstuids = UIDSet.toArray(cuid.dst); + // map source UIDs to Message objects + // XXX - could inline/optimize this + Message[] srcmsgs = getMessagesByUID(srcuids); + AppendUID[] result = new AppendUID[msgs.length]; + for (int i = 0; i < msgs.length; i++) { + int j = i; + do { + if (msgs[i] == srcmsgs[j]) { + result[i] = new AppendUID( + cuid.uidvalidity, dstuids[j]); + break; + } + j++; + if (j >= srcmsgs.length) + j = 0; + } while (j != i); + } + return result; + } catch (CommandFailedException cfx) { + if (cfx.getMessage().indexOf("TRYCREATE") != -1) + throw new FolderNotFoundException( + folder, + folder.getFullName() + " does not exist" + ); + else + throw new MessagingException(cfx.getMessage(), cfx); + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + } + + /** + * Expunge all messages marked as DELETED. + */ + @Override + public synchronized Message[] expunge() throws MessagingException { + return expunge(null); + } + + /** + * Expunge the indicated messages, which must have been marked as DELETED. + * + * Depends on the UIDPLUS extension - + * RFC 4315. + * + * @param msgs the messages to expunge + * @return the expunged messages + * @exception MessagingException for failures + */ + public synchronized Message[] expunge(Message[] msgs) + throws MessagingException { + checkOpened(); + + if (msgs != null) { + // call fetch to make sure we have all the UIDs + FetchProfile fp = new FetchProfile(); + fp.add(UIDFolder.FetchProfileItem.UID); + fetch(msgs, fp); + } + + IMAPMessage[] rmsgs; + synchronized(messageCacheLock) { + doExpungeNotification = false; // We do this ourselves later + try { + IMAPProtocol p = getProtocol(); + if (msgs != null) + p.uidexpunge(Utility.toUIDSet(msgs)); + else + p.expunge(); + } catch (CommandFailedException cfx) { + // expunge not allowed, perhaps due to a permission problem? + if (mode != READ_WRITE) + throw new IllegalStateException( + "Cannot expunge READ_ONLY folder: " + fullName); + else + throw new MessagingException(cfx.getMessage(), cfx); + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + // Bad bad server .. + throw new MessagingException(pex.getMessage(), pex); + } finally { + doExpungeNotification = true; + } + + // Cleanup expunged messages and sync messageCache with reality. + if (msgs != null) + rmsgs = messageCache.removeExpungedMessages(msgs); + else + rmsgs = messageCache.removeExpungedMessages(); + if (uidTable != null) { + for (int i = 0; i < rmsgs.length; i++) { + IMAPMessage m = rmsgs[i]; + /* remove this message from the UIDTable */ + long uid = m.getUID(); + if (uid != -1) + uidTable.remove(Long.valueOf(uid)); + } + } + + // Update 'total' + total = messageCache.size(); + } + + // Notify listeners. This time its for real, guys. + if (rmsgs.length > 0) + notifyMessageRemovedListeners(true, rmsgs); + return rmsgs; + } + + /** + * Search whole folder for messages matching the given term. + * If the property mail.imap.throwsearchexception is true, + * and the search term is too complex for the IMAP protocol, + * SearchException is thrown. Otherwise, if the search term is too + * complex, super.search is called to do the search on + * the client. + * + * @param term the search term + * @return the messages that match + * @exception SearchException if mail.imap.throwsearchexception is + * true and the search is too complex for the IMAP protocol + * @exception MessagingException for other failures + */ + @Override + public synchronized Message[] search(SearchTerm term) + throws MessagingException { + checkOpened(); + + try { + Message[] matchMsgs = null; + + synchronized(messageCacheLock) { + int[] matches = getProtocol().search(term); + if (matches != null) + matchMsgs = getMessagesBySeqNumbers(matches); + } + return matchMsgs; + + } catch (CommandFailedException cfx) { + // unsupported charset or search criterion + return super.search(term); + } catch (SearchException sex) { + // too complex for IMAP + if (((IMAPStore)store).throwSearchException()) + throw sex; + return super.search(term); + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + // bug in our IMAP layer ? + throw new MessagingException(pex.getMessage(), pex); + } + } + + /** + * Search the folder for messages matching the given term. Returns + * array of matching messages. Returns an empty array if no matching + * messages are found. + */ + @Override + public synchronized Message[] search(SearchTerm term, Message[] msgs) + throws MessagingException { + checkOpened(); + + if (msgs.length == 0) + // need to return an empty array (not null!) + return msgs; + + try { + Message[] matchMsgs = null; + + synchronized(messageCacheLock) { + IMAPProtocol p = getProtocol(); + MessageSet[] ms = Utility.toMessageSetSorted(msgs, null); + if (ms == null) + throw new MessageRemovedException( + "Messages have been removed"); + int[] matches = p.search(ms, term); + if (matches != null) + matchMsgs = getMessagesBySeqNumbers(matches); + } + return matchMsgs; + + } catch (CommandFailedException cfx) { + // unsupported charset or search criterion + return super.search(term, msgs); + } catch (SearchException sex) { + // too complex for IMAP + return super.search(term, msgs); + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + // bug in our IMAP layer ? + throw new MessagingException(pex.getMessage(), pex); + } + } + + /** + * Sort the messages in the folder according to the sort criteria. + * The messages are returned in the sorted order, but the order of + * the messages in the folder is not changed.

    + * + * Depends on the SORT extension - + * RFC 5256. + * + * @param term the SortTerms + * @return the messages in sorted order + * @exception MessagingException for failures + * @since JavaMail 1.4.4 + */ + public synchronized Message[] getSortedMessages(SortTerm[] term) + throws MessagingException { + return getSortedMessages(term, null); + } + + /** + * Sort the messages in the folder according to the sort criteria. + * The messages are returned in the sorted order, but the order of + * the messages in the folder is not changed. Only messages matching + * the search criteria are considered.

    + * + * Depends on the SORT extension - + * RFC 5256. + * + * @param term the SortTerms + * @param sterm the SearchTerm + * @return the messages in sorted order + * @exception MessagingException for failures + * @since JavaMail 1.4.4 + */ + public synchronized Message[] getSortedMessages(SortTerm[] term, + SearchTerm sterm) throws MessagingException { + checkOpened(); + + try { + Message[] matchMsgs = null; + + synchronized(messageCacheLock) { + int[] matches = getProtocol().sort(term, sterm); + if (matches != null) + matchMsgs = getMessagesBySeqNumbers(matches); + } + return matchMsgs; + + } catch (CommandFailedException cfx) { + // unsupported charset or search criterion + throw new MessagingException(cfx.getMessage(), cfx); + } catch (SearchException sex) { + // too complex for IMAP + throw new MessagingException(sex.getMessage(), sex); + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + // bug in our IMAP layer ? + throw new MessagingException(pex.getMessage(), pex); + } + } + + /* + * Override Folder method to keep track of whether we have any + * message count listeners. Normally we won't have any, so we + * can avoid creating message objects to pass to the notify + * method. It's too hard to keep track of when all listeners + * are removed, and that's a rare case, so we don't try. + */ + @Override + public synchronized void addMessageCountListener(MessageCountListener l) { + super.addMessageCountListener(l); + hasMessageCountListener = true; + } + + /*********************************************************** + * UIDFolder interface methods + **********************************************************/ + + /** + * Returns the UIDValidity for this folder. + */ + @Override + public synchronized long getUIDValidity() throws MessagingException { + if (opened) // we already have this information + return uidvalidity; + + IMAPProtocol p = null; + Status status = null; + + try { + p = getStoreProtocol(); // XXX + String[] item = { "UIDVALIDITY" }; + status = p.status(fullName, item); + } catch (BadCommandException bex) { + // Probably a RFC1730 server + throw new MessagingException("Cannot obtain UIDValidity", bex); + } catch (ConnectionException cex) { + // Oops, the store or folder died on us. + throwClosedException(cex); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + + if (status == null) + throw new MessagingException("Cannot obtain UIDValidity"); + return status.uidvalidity; + } + + /** + * Returns the predicted UID that will be assigned to the + * next message that is appended to this folder. + * If the folder is closed, the STATUS command is used to + * retrieve this value. If the folder is open, the value + * returned from the SELECT or EXAMINE command is returned. + * Note that messages may have been appended to the folder + * while it was open and thus this value may be out of + * date.

    + * + * Servers implementing RFC2060 likely won't return this value + * when a folder is opened. Servers implementing RFC3501 + * should return this value when a folder is opened.

    + * + * @return the UIDNEXT value, or -1 if unknown + * @exception MessagingException for failures + * @since JavaMail 1.3.3 + */ + @Override + public synchronized long getUIDNext() throws MessagingException { + if (opened) // we already have this information + return uidnext; + + IMAPProtocol p = null; + Status status = null; + + try { + p = getStoreProtocol(); // XXX + String[] item = { "UIDNEXT" }; + status = p.status(fullName, item); + } catch (BadCommandException bex) { + // Probably a RFC1730 server + throw new MessagingException("Cannot obtain UIDNext", bex); + } catch (ConnectionException cex) { + // Oops, the store or folder died on us. + throwClosedException(cex); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + + if (status == null) + throw new MessagingException("Cannot obtain UIDNext"); + return status.uidnext; + } + + /** + * Get the Message corresponding to the given UID. + * If no such message exists, null is returned. + */ + @Override + public synchronized Message getMessageByUID(long uid) + throws MessagingException { + checkOpened(); // insure folder is open + + IMAPMessage m = null; + + try { + synchronized(messageCacheLock) { + Long l = Long.valueOf(uid); + + if (uidTable != null) { + // Check in uidTable + m = uidTable.get(l); + if (m != null) // found it + return m; + } else + uidTable = new Hashtable<>(); + + // Check with the server + // Issue UID FETCH command + getProtocol().fetchSequenceNumber(uid); + + if (uidTable != null) { + // Check in uidTable + m = uidTable.get(l); + if (m != null) // found it + return m; + } + } + } catch(ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + + return m; + } + + /** + * Get the Messages specified by the given range.

    + * Returns Message objects for all valid messages in this range. + * Returns an empty array if no messages are found. + */ + @Override + public synchronized Message[] getMessagesByUID(long start, long end) + throws MessagingException { + checkOpened(); // insure that folder is open + + Message[] msgs; // array of messages to be returned + + try { + synchronized(messageCacheLock) { + if (uidTable == null) + uidTable = new Hashtable<>(); + + // Issue UID FETCH for given range + long[] ua = getProtocol().fetchSequenceNumbers(start, end); + + List ma = new ArrayList<>(); + // NOTE: Below must be within messageCacheLock region + for (int i = 0; i < ua.length; i++) { + Message m = uidTable.get(Long.valueOf(ua[i])); + if (m != null) // found it + ma.add(m); + } + msgs = ma.toArray(new Message[ma.size()]); + } + } catch(ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + + return msgs; + } + + /** + * Get the Messages specified by the given array.

    + * + * uids.length() elements are returned. + * If any UID in the array is invalid, a null entry + * is returned for that element. + */ + @Override + public synchronized Message[] getMessagesByUID(long[] uids) + throws MessagingException { + checkOpened(); // insure that folder is open + + try { + synchronized(messageCacheLock) { + long[] unavailUids = uids; + if (uidTable != null) { + // to collect unavailable UIDs + List v = new ArrayList<>(); + for (long uid : uids) { + if (!uidTable.containsKey(uid)) { + // This UID has not been loaded yet. + v.add(uid); + } + } + + int vsize = v.size(); + unavailUids = new long[vsize]; + for (int i = 0; i < vsize; i++) { + unavailUids[i] = v.get(i); + } + } else + uidTable = new Hashtable<>(); + + if (unavailUids.length > 0) { + // Issue UID FETCH request for given uids + getProtocol().fetchSequenceNumbers(unavailUids); + } + + // Return array of size = uids.length + Message[] msgs = new Message[uids.length]; + for (int i = 0; i < uids.length; i++) + msgs[i] = (Message)uidTable.get(Long.valueOf(uids[i])); + return msgs; + } + } catch(ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + + /** + * Get the UID for the specified message. + */ + @Override + public synchronized long getUID(Message message) + throws MessagingException { + if (message.getFolder() != this) + throw new NoSuchElementException( + "Message does not belong to this folder"); + + checkOpened(); // insure that folder is open + + if (!(message instanceof IMAPMessage)) + throw new MessagingException("message is not an IMAPMessage"); + IMAPMessage m = (IMAPMessage)message; + // If the message already knows its UID, great .. + long uid; + if ((uid = m.getUID()) != -1) + return uid; + + synchronized(messageCacheLock) { // Acquire Lock + try { + IMAPProtocol p = getProtocol(); + m.checkExpunged(); // insure that message is not expunged + UID u = p.fetchUID(m.getSequenceNumber()); + + if (u != null) { + uid = u.uid; + m.setUID(uid); // set message's UID + + // insert this message into uidTable + if (uidTable == null) + uidTable = new Hashtable<>(); + uidTable.put(Long.valueOf(uid), m); + } + } catch (ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + + return uid; + } + + /** + * Servers that support the UIDPLUS extension + * (RFC 4315) + * may indicate that this folder does not support persistent UIDs; + * that is, UIDVALIDITY will be different each time the folder is + * opened. Only valid when the folder is open. + * + * @return true if UIDs are not sticky + * @exception MessagingException for failures + * @exception IllegalStateException if the folder isn't open + * @see "RFC 4315" + * @since JavaMail 1.6.0 + */ + public synchronized boolean getUIDNotSticky() throws MessagingException { + checkOpened(); + return uidNotSticky; + } + + /** + * Get or create Message objects for the UIDs. + */ + private Message[] createMessagesForUIDs(long[] uids) { + IMAPMessage[] msgs = new IMAPMessage[uids.length]; + for (int i = 0; i < uids.length; i++) { + IMAPMessage m = null; + if (uidTable != null) + m = uidTable.get(Long.valueOf(uids[i])); + if (m == null) { + // fake it, we don't know what message this really is + m = newIMAPMessage(-1); // no sequence number + m.setUID(uids[i]); + m.setExpunged(true); + } + msgs[i++] = m; + } + return msgs; + } + + /** + * Returns the HIGHESTMODSEQ for this folder. + * + * @return the HIGHESTMODSEQ value + * @exception MessagingException for failures + * @see "RFC 4551" + * @since JavaMail 1.5.1 + */ + public synchronized long getHighestModSeq() throws MessagingException { + if (opened) // we already have this information + return highestmodseq; + + IMAPProtocol p = null; + Status status = null; + + try { + p = getStoreProtocol(); // XXX + if (!p.hasCapability("CONDSTORE")) + throw new BadCommandException("CONDSTORE not supported"); + String[] item = { "HIGHESTMODSEQ" }; + status = p.status(fullName, item); + } catch (BadCommandException bex) { + // Probably a RFC1730 server + throw new MessagingException("Cannot obtain HIGHESTMODSEQ", bex); + } catch (ConnectionException cex) { + // Oops, the store or folder died on us. + throwClosedException(cex); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + + if (status == null) + throw new MessagingException("Cannot obtain HIGHESTMODSEQ"); + return status.highestmodseq; + } + + /** + * Get the messages that have been changed since the given MODSEQ value. + * Also, prefetch the flags for the messages.

    + * + * The server must support the CONDSTORE extension. + * + * @param start the first message number + * @param end the last message number + * @param modseq the MODSEQ value + * @return the changed messages + * @exception MessagingException for failures + * @see "RFC 4551" + * @since JavaMail 1.5.1 + */ + public synchronized Message[] getMessagesByUIDChangedSince( + long start, long end, long modseq) + throws MessagingException { + checkOpened(); // insure that folder is open + + try { + synchronized (messageCacheLock) { + IMAPProtocol p = getProtocol(); + if (!p.hasCapability("CONDSTORE")) + throw new BadCommandException("CONDSTORE not supported"); + + // Issue FETCH for given range + int[] nums = p.uidfetchChangedSince(start, end, modseq); + return getMessagesBySeqNumbers(nums); + } + } catch(ConnectionException cex) { + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + + /** + * Get the quotas for the quotaroot associated with this + * folder. Note that many folders may have the same quotaroot. + * Quotas are controlled on the basis of a quotaroot, not + * (necessarily) a folder. The relationship between folders + * and quotaroots depends on the IMAP server. Some servers + * might implement a single quotaroot for all folders owned by + * a user. Other servers might implement a separate quotaroot + * for each folder. A single folder can even have multiple + * quotaroots, perhaps controlling quotas for different + * resources. + * + * @return array of Quota objects for the quotaroots associated with + * this folder + * @exception MessagingException if the server doesn't support the + * QUOTA extension + */ + public Quota[] getQuota() throws MessagingException { + return (Quota[])doOptionalCommand("QUOTA not supported", + new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + return p.getQuotaRoot(fullName); + } + }); + } + + /** + * Set the quotas for the quotaroot specified in the quota argument. + * Typically this will be one of the quotaroots associated with this + * folder, as obtained from the getQuota method, but it + * need not be. + * + * @param quota the quota to set + * @exception MessagingException if the server doesn't support the + * QUOTA extension + */ + public void setQuota(final Quota quota) throws MessagingException { + doOptionalCommand("QUOTA not supported", + new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + p.setQuota(quota); + return null; + } + }); + } + + /** + * Get the access control list entries for this folder. + * + * @return array of access control list entries + * @exception MessagingException if the server doesn't support the + * ACL extension + */ + public ACL[] getACL() throws MessagingException { + return (ACL[])doOptionalCommand("ACL not supported", + new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + return p.getACL(fullName); + } + }); + } + + /** + * Add an access control list entry to the access control list + * for this folder. + * + * @param acl the access control list entry to add + * @exception MessagingException if the server doesn't support the + * ACL extension + */ + public void addACL(ACL acl) throws MessagingException { + setACL(acl, '\0'); + } + + /** + * Remove any access control list entry for the given identifier + * from the access control list for this folder. + * + * @param name the identifier for which to remove all ACL entries + * @exception MessagingException if the server doesn't support the + * ACL extension + */ + public void removeACL(final String name) throws MessagingException { + doOptionalCommand("ACL not supported", + new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + p.deleteACL(fullName, name); + return null; + } + }); + } + + /** + * Add the rights specified in the ACL to the entry for the + * identifier specified in the ACL. If an entry for the identifier + * doesn't already exist, add one. + * + * @param acl the identifer and rights to add + * @exception MessagingException if the server doesn't support the + * ACL extension + */ + public void addRights(ACL acl) throws MessagingException { + setACL(acl, '+'); + } + + /** + * Remove the rights specified in the ACL from the entry for the + * identifier specified in the ACL. + * + * @param acl the identifer and rights to remove + * @exception MessagingException if the server doesn't support the + * ACL extension + */ + public void removeRights(ACL acl) throws MessagingException { + setACL(acl, '-'); + } + + /** + * Get all the rights that may be allowed to the given identifier. + * Rights are grouped per RFC 2086 and each group is returned as an + * element of the array. The first element of the array is the set + * of rights that are always granted to the identifier. Later + * elements are rights that may be optionally granted to the + * identifier.

    + * + * Note that this method lists the rights that it is possible to + * assign to the given identifier, not the rights that are + * actually granted to the given identifier. For the latter, see + * the getACL method. + * + * @param name the identifier to list rights for + * @return array of Rights objects representing possible + * rights for the identifier + * @exception MessagingException if the server doesn't support the + * ACL extension + */ + public Rights[] listRights(final String name) throws MessagingException { + return (Rights[])doOptionalCommand("ACL not supported", + new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + return p.listRights(fullName, name); + } + }); + } + + /** + * Get the rights allowed to the currently authenticated user. + * + * @return the rights granted to the current user + * @exception MessagingException if the server doesn't support the + * ACL extension + */ + public Rights myRights() throws MessagingException { + return (Rights)doOptionalCommand("ACL not supported", + new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + return p.myRights(fullName); + } + }); + } + + private void setACL(final ACL acl, final char mod) + throws MessagingException { + doOptionalCommand("ACL not supported", + new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + p.setACL(fullName, mod, acl); + return null; + } + }); + } + + /** + * Get the attributes that the IMAP server returns with the + * LIST response. + * + * @return array of attributes for this folder + * @exception MessagingException for failures + * @since JavaMail 1.3.3 + */ + public synchronized String[] getAttributes() throws MessagingException { + checkExists(); + if (attributes == null) + exists(); // do a LIST to set the attributes + return attributes == null ? new String[0] : attributes.clone(); + } + + /** + * Use the IMAP IDLE command (see + * RFC 2177), + * if supported by the server, to enter idle mode so that the server + * can send unsolicited notifications of new messages arriving, etc. + * without the need for the client to constantly poll the server. + * Use an appropriate listener to be notified of new messages or + * other events. When another thread (e.g., the listener thread) + * needs to issue an IMAP comand for this folder, the idle mode will + * be terminated and this method will return. Typically the caller + * will invoke this method in a loop.

    + * + * The mail.imap.minidletime property enforces a minimum delay + * before returning from this method, to ensure that other threads + * have a chance to issue commands before the caller invokes this + * method again. The default delay is 10 milliseconds. + * + * @exception MessagingException if the server doesn't support the + * IDLE extension + * @exception IllegalStateException if the folder isn't open + * + * @since JavaMail 1.4.1 + */ + public void idle() throws MessagingException { + idle(false); + } + + /** + * Like {@link #idle}, but if once is true, abort the + * IDLE command after the first notification, to allow the caller + * to process any notification synchronously. + * + * @param once only do one notification? + * @exception MessagingException if the server doesn't support the + * IDLE extension + * @exception IllegalStateException if the folder isn't open + * + * @since JavaMail 1.4.3 + */ + public void idle(boolean once) throws MessagingException { + synchronized (this) { + /* + * We can't support the idle method if we're using SocketChannels + * because SocketChannels don't allow simultaneous read and write. + * If we're blocked in a read waiting for IDLE responses, we can't + * send the DONE message to abort the IDLE. Sigh. + * XXX - We could do select here too, like IdleManager, instead + * of blocking in read, but that's more complicated. + */ + if (protocol != null && protocol.getChannel() != null) + throw new MessagingException( + "idle method not supported with SocketChannels"); + } + if (!startIdle(null)) + return; + + /* + * We gave up the folder lock so that other threads + * can get into the folder far enough to see that we're + * in IDLE and abort the IDLE. + * + * Now we read responses from the IDLE command, especially + * including unsolicited notifications from the server. + * We don't hold the messageCacheLock while reading because + * it protects the idleState and other threads need to be + * able to examine the state. + * + * The messageCacheLock is held in handleIdle while processing + * the responses so that we can update the number of messages + * in the folder (for example). + */ + for (;;) { + if (!handleIdle(once)) + break; + } + + /* + * Enforce a minimum delay to give time to threads + * processing the responses that came in while we + * were idle. + */ + int minidle = ((IMAPStore)store).getMinIdleTime(); + if (minidle > 0) { + try { + Thread.sleep(minidle); + } catch (InterruptedException ex) { + // restore the interrupted state, which callers might depend on + Thread.currentThread().interrupt(); + } + } + } + + /** + * Start the IDLE command and put this folder into the IDLE state. + * IDLE processing is done later in handleIdle(), e.g., called from + * the IdleManager. + * + * @return true if IDLE started, false otherwise + * @exception MessagingException if the server doesn't support the + * IDLE extension + * @exception IllegalStateException if the folder isn't open + * @since JavaMail 1.5.2 + */ + boolean startIdle(final IdleManager im) throws MessagingException { + // ASSERT: Must NOT be called with this folder's + // synchronization lock held. + assert !Thread.holdsLock(this); + synchronized(this) { + checkOpened(); + if (im != null && idleManager != null && im != idleManager) + throw new MessagingException( + "Folder already being watched by another IdleManager"); + Boolean started = (Boolean)doOptionalCommand("IDLE not supported", + new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + // if the IdleManager is already watching this folder, + // there's nothing to do here + if (idleState == IDLE && + im != null && im == idleManager) + return Boolean.TRUE; // already watching it + if (idleState == RUNNING) { + p.idleStart(); + logger.finest("startIdle: set to IDLE"); + idleState = IDLE; + idleManager = im; + return Boolean.TRUE; + } else { + // some other thread must be running the IDLE + // command, we'll just wait for it to finish + // without aborting it ourselves + try { + // give up lock and wait to be not idle + messageCacheLock.wait(); + } catch (InterruptedException ex) { + // restore the interrupted state, which callers + // might depend on + Thread.currentThread().interrupt(); + } + return Boolean.FALSE; + } + } + }); + logger.log(Level.FINEST, "startIdle: return {0}", started); + return started.booleanValue(); + } + } + + /** + * Read a response from the server while we're in the IDLE state. + * We hold the messageCacheLock while processing the + * responses so that we can update the number of messages + * in the folder (for example). + * + * @param once only do one notification? + * @return true if we should look for more IDLE responses, + * false if IDLE is done + * @exception MessagingException for errors + * @since JavaMail 1.5.2 + */ + boolean handleIdle(boolean once) throws MessagingException { + Response r = null; + do { + r = protocol.readIdleResponse(); + try { + synchronized (messageCacheLock) { + if (r.isBYE() && r.isSynthetic() && idleState == IDLE) { + /* + * If it was a timeout and no bytes were transferred + * we ignore it and go back and read again. + * If the I/O was otherwise interrupted, and no + * bytes were transferred, we take it as a request + * to abort the IDLE. + */ + Exception ex = r.getException(); + if (ex instanceof InterruptedIOException && + ((InterruptedIOException)ex). + bytesTransferred == 0) { + if (ex instanceof SocketTimeoutException) { + logger.finest( + "handleIdle: ignoring socket timeout"); + r = null; // repeat do/while loop + } else { + logger.finest("handleIdle: interrupting IDLE"); + IdleManager im = idleManager; + if (im != null) { + logger.finest( + "handleIdle: request IdleManager to abort"); + im.requestAbort(this); + } else { + logger.finest("handleIdle: abort IDLE"); + protocol.idleAbort(); + idleState = ABORTING; + } + // normally will exit the do/while loop + } + continue; + } + } + boolean done = true; + try { + if (protocol == null || + !protocol.processIdleResponse(r)) + return false; // done + done = false; + } finally { + if (done) { + logger.finest("handleIdle: set to RUNNING"); + idleState = RUNNING; + idleManager = null; + messageCacheLock.notifyAll(); + } + } + if (once) { + if (idleState == IDLE) { + try { + protocol.idleAbort(); + } catch (Exception ex) { + // ignore any failures, still have to abort. + // connection failures will be detected above + // in the call to readIdleResponse. + } + idleState = ABORTING; + } + } + } + } catch (ConnectionException cex) { + // Oops, the folder died on us. + throw new FolderClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + // keep processing responses already in our buffer + } while (r == null || protocol.hasResponse()); + return true; + } + + /* + * If an IDLE command is in progress, abort it if necessary, + * and wait until it completes. + * ASSERT: Must be called with the message cache lock held. + */ + void waitIfIdle() throws ProtocolException { + assert Thread.holdsLock(messageCacheLock); + while (idleState != RUNNING) { + if (idleState == IDLE) { + IdleManager im = idleManager; + if (im != null) { + logger.finest("waitIfIdle: request IdleManager to abort"); + im.requestAbort(this); + } else { + logger.finest("waitIfIdle: abort IDLE"); + protocol.idleAbort(); + idleState = ABORTING; + } + } else + logger.log(Level.FINEST, "waitIfIdle: idleState {0}", idleState); + try { + // give up lock and wait to be not idle + if (logger.isLoggable(Level.FINEST)) + logger.finest("waitIfIdle: wait to be not idle: " + + Thread.currentThread()); + messageCacheLock.wait(); + if (logger.isLoggable(Level.FINEST)) + logger.finest("waitIfIdle: wait done, idleState " + + idleState + ": " + Thread.currentThread()); + } catch (InterruptedException ex) { + // restore the interrupted state, which callers might depend on + Thread.currentThread().interrupt(); + // If someone is trying to interrupt us we can't keep going + // around the loop waiting for IDLE to complete, but we can't + // just return because callers expect the idleState to be + // RUNNING when we return. Throwing this exception seems + // like the best choice. + throw new ProtocolException("Interrupted waitIfIdle", ex); + } + } + } + + /* + * Send the DONE command that aborts the IDLE; used by IdleManager. + */ + void idleAbort() { + synchronized (messageCacheLock) { + if (idleState == IDLE && protocol != null) { + protocol.idleAbort(); + idleState = ABORTING; + } + } + } + + /* + * Send the DONE command that aborts the IDLE and wait for the response; + * used by IdleManager. + */ + void idleAbortWait() { + synchronized (messageCacheLock) { + if (idleState == IDLE && protocol != null) { + protocol.idleAbort(); + idleState = ABORTING; + + // read responses until OK or connection failure + try { + for (;;) { + if (!handleIdle(false)) + break; + } + } catch (Exception ex) { + // assume it's a connection failure; nothing more to do + logger.log(Level.FINEST, "Exception in idleAbortWait", ex); + } + logger.finest("IDLE aborted"); + } + } + } + + /** + * Return the SocketChannel for this connection, if any, for use + * in IdleManager. + */ + SocketChannel getChannel() { + return protocol != null ? protocol.getChannel() : null; + } + + /** + * Send the IMAP ID command (if supported by the server) and return + * the result from the server. The ID command identfies the client + * to the server and returns information about the server to the client. + * See RFC 2971. + * The returned Map is unmodifiable. + * + * @param clientParams a Map of keys and values identifying the client + * @return a Map of keys and values identifying the server + * @exception MessagingException if the server doesn't support the + * ID extension + * @since JavaMail 1.5.1 + */ + @SuppressWarnings("unchecked") + public Map id(final Map clientParams) + throws MessagingException { + checkOpened(); + return (Map)doOptionalCommand("ID not supported", + new ProtocolCommand() { + @Override + public Object doCommand(IMAPProtocol p) + throws ProtocolException { + return p.id(clientParams); + } + }); + } + + /** + * Use the IMAP STATUS command to get the indicated item. + * The STATUS item may be a standard item such as "RECENT" or "UNSEEN", + * or may be a server-specific item. + * The folder must be closed. If the item is not found, or the + * folder is open, -1 is returned. + * + * @param item the STATUS item to fetch + * @return the value of the STATUS item, or -1 + * @exception MessagingException for errors + * @since JavaMail 1.5.2 + */ + public synchronized long getStatusItem(String item) + throws MessagingException { + if (!opened) { + checkExists(); + + IMAPProtocol p = null; + Status status = null; + try { + p = getStoreProtocol(); // XXX + String[] items = { item }; + status = p.status(fullName, items); + return status != null ? status.getItem(item) : -1; + } catch (BadCommandException bex) { + // doesn't support STATUS, probably vanilla IMAP4 .. + // Could EXAMINE, SEARCH for UNREAD messages and + // return the count .. bah, not worth it. + return -1; + } catch (ConnectionException cex) { + throw new StoreClosedException(store, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + } + return -1; + } + + /** + * The response handler. This is the callback routine that is + * invoked by the protocol layer. + */ + /* + * ASSERT: This method must be called only when holding the + * messageCacheLock. + * ASSERT: This method must *not* invoke any other method that + * might grab the 'folder' lock or 'message' lock (i.e., any + * synchronized methods on IMAPFolder or IMAPMessage) + * since that will result in violating the locking hierarchy. + */ + @Override + public void handleResponse(Response r) { + assert Thread.holdsLock(messageCacheLock); + + /* + * First, delegate possible ALERT or notification to the Store. + */ + if (r.isOK() || r.isNO() || r.isBAD() || r.isBYE()) + ((IMAPStore)store).handleResponseCode(r); + + /* + * Now check whether this is a BYE or OK response and + * handle appropriately. + */ + if (r.isBYE()) { + if (opened) // XXX - accessed without holding folder lock + cleanup(false); + return; + } else if (r.isOK()) { + // HIGHESTMODSEQ can be updated on any OK response + r.skipSpaces(); + if (r.readByte() == '[') { + String s = r.readAtom(); + if (s.equalsIgnoreCase("HIGHESTMODSEQ")) + highestmodseq = r.readLong(); + } + r.reset(); + return; + } else if (!r.isUnTagged()) { + return; // might be a continuation for IDLE + } + + /* Now check whether this is an IMAP specific response */ + if (!(r instanceof IMAPResponse)) { + // Probably a bug in our code ! + // XXX - should be an assert + logger.fine("UNEXPECTED RESPONSE : " + r.toString()); + return; + } + + IMAPResponse ir = (IMAPResponse)r; + + if (ir.keyEquals("EXISTS")) { // EXISTS + int exists = ir.getNumber(); + if (exists <= realTotal) + // Could be the EXISTS following EXPUNGE, ignore 'em + return; + + int count = exists - realTotal; // number of new messages + Message[] msgs = new Message[count]; + + // Add 'count' new IMAPMessage objects into the messageCache + messageCache.addMessages(count, realTotal + 1); + int oldtotal = total; // used in loop below + realTotal += count; + total += count; + + // avoid instantiating Message objects if no listeners. + if (hasMessageCountListener) { + for (int i = 0; i < count; i++) + msgs[i] = messageCache.getMessage(++oldtotal); + + // Notify listeners. + notifyMessageAddedListeners(msgs); + } + + } else if (ir.keyEquals("EXPUNGE")) { + // EXPUNGE response. + + int seqnum = ir.getNumber(); + if (seqnum > realTotal) { + // A message was expunged that we never knew about. + // Exchange will do this. Just ignore the notification. + // (Alternatively, we could simulate an EXISTS for the + // expunged message before expunging it.) + return; + } + Message[] msgs = null; + if (doExpungeNotification && hasMessageCountListener) { + // save the Message object first; can't look it + // up after it's expunged + msgs = new Message[] { getMessageBySeqNumber(seqnum) }; + if (msgs[0] == null) // XXX - should never happen + msgs = null; + } + + messageCache.expungeMessage(seqnum); + + // decrement 'realTotal'; but leave 'total' unchanged + realTotal--; + + if (msgs != null) // Do the notification here. + notifyMessageRemovedListeners(false, msgs); + + } else if (ir.keyEquals("VANISHED")) { + // after the folder is opened with QRESYNC, a VANISHED response + // without the (EARLIER) tag is used instead of the EXPUNGE + // response + + // "VANISHED" SP ["(EARLIER)"] SP known-uids + String[] s = ir.readAtomStringList(); + if (s == null) { // no (EARLIER) + String uids = ir.readAtom(); + UIDSet[] uidset = UIDSet.parseUIDSets(uids); + // assume no duplicates and no UIDs out of range + realTotal -= UIDSet.size(uidset); + long[] luid = UIDSet.toArray(uidset); + Message[] msgs = createMessagesForUIDs(luid); + for (Message m : msgs) { + if (m.getMessageNumber() > 0) + messageCache.expungeMessage(m.getMessageNumber()); + } + if (doExpungeNotification && hasMessageCountListener) { + notifyMessageRemovedListeners(true, msgs); + } + } // else if (EARLIER), ignore + + } else if (ir.keyEquals("FETCH")) { + assert ir instanceof FetchResponse : "!ir instanceof FetchResponse"; + Message msg = processFetchResponse((FetchResponse)ir); + if (msg != null) + notifyMessageChangedListeners( + MessageChangedEvent.FLAGS_CHANGED, msg); + + } else if (ir.keyEquals("RECENT")) { + // update 'recent' + recent = ir.getNumber(); + } + } + + /** + * Process a FETCH response. + * The only unsolicited FETCH response that makes sense + * to me (for now) is FLAGS updates, which might include + * UID and MODSEQ information. Ignore any other junk. + */ + private Message processFetchResponse(FetchResponse fr) { + IMAPMessage msg = getMessageBySeqNumber(fr.getNumber()); + if (msg != null) { // should always be true + boolean notify = false; + + UID uid = fr.getItem(UID.class); + if (uid != null && msg.getUID() != uid.uid) { + msg.setUID(uid.uid); + if (uidTable == null) + uidTable = new Hashtable<>(); + uidTable.put(Long.valueOf(uid.uid), msg); + notify = true; + } + + MODSEQ modseq = fr.getItem(MODSEQ.class); + if (modseq != null && msg._getModSeq() != modseq.modseq) { + msg.setModSeq(modseq.modseq); + /* + * XXX - should we update the folder's HIGHESTMODSEQ or not? + * + if (modseq.modseq > highestmodseq) + highestmodseq = modseq.modseq; + */ + notify = true; + } + + // Get FLAGS response, if present + FLAGS flags = fr.getItem(FLAGS.class); + if (flags != null) { + msg._setFlags(flags); // assume flags changed + notify = true; + } + + // handle any extension items that might've changed + // XXX - no notifications associated with extension items + msg.handleExtensionFetchItems(fr.getExtensionItems()); + + if (!notify) + msg = null; + } + return msg; + } + + /** + * Handle the given array of Responses. + * + * ASSERT: This method must be called only when holding the + * messageCacheLock + */ + void handleResponses(Response[] r) { + for (int i = 0; i < r.length; i++) { + if (r[i] != null) + handleResponse(r[i]); + } + } + + /** + * Get this folder's Store's protocol connection. + * + * When acquiring a store protocol object, it is important to + * use the following steps: + * + *

    +     *     IMAPProtocol p = null;
    +     *     try {
    +     *         p = getStoreProtocol();
    +     *         // perform the command
    +     *     } catch (WhateverException ex) {
    +     *         // handle it
    +     *     } finally {
    +     *         releaseStoreProtocol(p);
    +     *     }
    +     * 
    + * + * ASSERT: Must be called with this folder's synchronization lock held. + * + * @return the IMAPProtocol for the Store's connection + * @exception ProtocolException for protocol errors + */ + protected synchronized IMAPProtocol getStoreProtocol() + throws ProtocolException { + connectionPoolLogger.fine("getStoreProtocol() borrowing a connection"); + return ((IMAPStore)store).getFolderStoreProtocol(); + } + + /** + * Throw the appropriate 'closed' exception. + * + * @param cex the ConnectionException + * @exception FolderClosedException if the folder is closed + * @exception StoreClosedException if the store is closed + */ + protected synchronized void throwClosedException(ConnectionException cex) + throws FolderClosedException, StoreClosedException { + // If it's the folder's protocol object, throw a FolderClosedException; + // otherwise, throw a StoreClosedException. + // If a command has failed because the connection is closed, + // the folder will have already been forced closed by the + // time we get here and our protocol object will have been + // released, so if we no longer have a protocol object we base + // this decision on whether we *think* the folder is open. + if ((protocol != null && cex.getProtocol() == protocol) || + (protocol == null && !reallyClosed)) + throw new FolderClosedException(this, cex.getMessage()); + else + throw new StoreClosedException(store, cex.getMessage()); + } + + /** + * Return the IMAPProtocol object for this folder.

    + * + * This method will block if necessary to wait for an IDLE + * command to finish. + * + * @return the IMAPProtocol object used when the folder is open + * @exception ProtocolException for protocol errors + */ + protected IMAPProtocol getProtocol() throws ProtocolException { + assert Thread.holdsLock(messageCacheLock); + waitIfIdle(); + // if we no longer have a protocol object after waiting, it probably + // means the connection has been closed due to a communnication error, + // or possibly because the folder has been closed + if (protocol == null) + throw new ConnectionException("Connection closed"); + return protocol; + } + + /** + * A simple interface for user-defined IMAP protocol commands. + */ + public static interface ProtocolCommand { + /** + * Execute the user-defined command using the supplied IMAPProtocol + * object. + * + * @param protocol the IMAPProtocol for the connection + * @return the results of the command + * @exception ProtocolException for protocol errors + */ + public Object doCommand(IMAPProtocol protocol) throws ProtocolException; + } + + /** + * Execute a user-supplied IMAP command. The command is executed + * in the appropriate context with the necessary locks held and + * using the appropriate IMAPProtocol object.

    + * + * This method returns whatever the ProtocolCommand + * object's doCommand method returns. If the + * doCommand method throws a ConnectionException + * it is translated into a StoreClosedException or + * FolderClosedException as appropriate. If the + * doCommand method throws a ProtocolException + * it is translated into a MessagingException.

    + * + * The following example shows how to execute the IMAP NOOP command. + * Executing more complex IMAP commands requires intimate knowledge + * of the com.sun.mail.iap and + * com.sun.mail.imap.protocol packages, best acquired by + * reading the source code. + * + *

    +     * import com.sun.mail.iap.*;
    +     * import com.sun.mail.imap.*;
    +     * import com.sun.mail.imap.protocol.*;
    +     *
    +     * ...
    +     *
    +     * IMAPFolder f = (IMAPFolder)folder;
    +     * Object val = f.doCommand(new IMAPFolder.ProtocolCommand() {
    +     *	public Object doCommand(IMAPProtocol p)
    +     *			throws ProtocolException {
    +     *	    p.simpleCommand("NOOP", null);
    +     *	    return null;
    +     *	}
    +     * });
    +     * 
    + *

    + * + * Here's a more complex example showing how to use the proposed + * IMAP SORT extension: + * + *

    +     * import com.sun.mail.iap.*;
    +     * import com.sun.mail.imap.*;
    +     * import com.sun.mail.imap.protocol.*;
    +     *
    +     * ...
    +     *
    +     * IMAPFolder f = (IMAPFolder)folder;
    +     * Object val = f.doCommand(new IMAPFolder.ProtocolCommand() {
    +     *	public Object doCommand(IMAPProtocol p)
    +     *			throws ProtocolException {
    +     *	    // Issue command
    +     *	    Argument args = new Argument();
    +     *	    Argument list = new Argument();
    +     *	    list.writeString("SUBJECT");
    +     *	    args.writeArgument(list);
    +     *	    args.writeString("UTF-8");
    +     *	    args.writeString("ALL");
    +     *	    Response[] r = p.command("SORT", args);
    +     *	    Response response = r[r.length-1];
    +     *
    +     *	    // Grab response
    +     *	    Vector v = new Vector();
    +     *	    if (response.isOK()) { // command succesful 
    +     *		for (int i = 0, len = r.length; i < len; i++) {
    +     *		    if (!(r[i] instanceof IMAPResponse))
    +     *			continue;
    +     *
    +     *		    IMAPResponse ir = (IMAPResponse)r[i];
    +     *		    if (ir.keyEquals("SORT")) {
    +     *			String num;
    +     *			while ((num = ir.readAtomString()) != null)
    +     *			    System.out.println(num);
    +     *			r[i] = null;
    +     *		    }
    +     *		}
    +     *	    }
    +     *
    +     *	    // dispatch remaining untagged responses
    +     *	    p.notifyResponseHandlers(r);
    +     *	    p.handleResult(response);
    +     *
    +     *	    return null;
    +     *	}
    +     * });
    +     * 
    + * + * @param cmd the protocol command + * @return the result of the command + * @exception MessagingException for failures + */ + public Object doCommand(ProtocolCommand cmd) throws MessagingException { + try { + return doProtocolCommand(cmd); + } catch (ConnectionException cex) { + // Oops, the store or folder died on us. + throwClosedException(cex); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + return null; + } + + public Object doOptionalCommand(String err, ProtocolCommand cmd) + throws MessagingException { + try { + return doProtocolCommand(cmd); + } catch (BadCommandException bex) { + throw new MessagingException(err, bex); + } catch (ConnectionException cex) { + // Oops, the store or folder died on us. + throwClosedException(cex); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + return null; + } + + public Object doCommandIgnoreFailure(ProtocolCommand cmd) + throws MessagingException { + try { + return doProtocolCommand(cmd); + } catch (CommandFailedException cfx) { + return null; + } catch (ConnectionException cex) { + // Oops, the store or folder died on us. + throwClosedException(cex); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + return null; + } + + protected synchronized Object doProtocolCommand(ProtocolCommand cmd) + throws ProtocolException { + /* + * Check whether we have a protocol object, not whether we're + * opened, to allow use of the exsting protocol object in the + * open method before the state is changed to "opened". + */ + if (protocol != null) { + synchronized (messageCacheLock) { + return cmd.doCommand(getProtocol()); + } + } + + // only get here if using store's connection + IMAPProtocol p = null; + + try { + p = getStoreProtocol(); + return cmd.doCommand(p); + } finally { + releaseStoreProtocol(p); + } + } + + /** + * Release the store protocol object. If we borrowed a protocol + * object from the connection pool, give it back. If we used our + * own protocol object, nothing to do. + * + * ASSERT: Must be called with this folder's synchronization lock held. + * + * @param p the IMAPProtocol object + */ + protected synchronized void releaseStoreProtocol(IMAPProtocol p) { + if (p != protocol) + ((IMAPStore)store).releaseFolderStoreProtocol(p); + else { + // XXX - should never happen + logger.fine("releasing our protocol as store protocol?"); + } + } + + /** + * Release the protocol object. + * + * ASSERT: This method must be called only when holding the + * messageCacheLock + * + * @param returnToPool return the protocol object to the pool? + */ + protected void releaseProtocol(boolean returnToPool) { + if (protocol != null) { + protocol.removeResponseHandler(this); + + if (returnToPool) + ((IMAPStore)store).releaseProtocol(this, protocol); + else { + protocol.disconnect(); // make sure it's disconnected + ((IMAPStore)store).releaseProtocol(this, null); + } + protocol = null; + } + } + + /** + * Issue a noop command for the connection if the connection has not been + * used in more than a second. If keepStoreAlive is true, + * also issue a noop over the store's connection. + * + * ASSERT: This method must be called only when holding the + * messageCacheLock + * + * @param keepStoreAlive keep the Store alive too? + * @exception ProtocolException for protocol errors + */ + protected void keepConnectionAlive(boolean keepStoreAlive) + throws ProtocolException { + + assert Thread.holdsLock(messageCacheLock); + if (protocol == null) // in case connection was closed + return; + if (System.currentTimeMillis() - protocol.getTimestamp() > 1000) { + waitIfIdle(); + if (protocol != null) + protocol.noop(); + } + + if (keepStoreAlive && ((IMAPStore)store).hasSeparateStoreConnection()) { + IMAPProtocol p = null; + try { + p = ((IMAPStore)store).getFolderStoreProtocol(); + if (System.currentTimeMillis() - p.getTimestamp() > 1000) + p.noop(); + } finally { + ((IMAPStore)store).releaseFolderStoreProtocol(p); + } + } + } + + /** + * Get the message object for the given sequence number. If + * none found, null is returned. + * + * ASSERT: This method must be called only when holding the + * messageCacheLock + * + * @param seqnum the message sequence number + * @return the IMAPMessage object + */ + protected IMAPMessage getMessageBySeqNumber(int seqnum) { + if (seqnum > messageCache.size()) { + // Microsoft Exchange will sometimes return message + // numbers that it has not yet notified the client + // about via EXISTS; ignore those messages here. + // GoDaddy IMAP does this too. + if (logger.isLoggable(Level.FINE)) + logger.fine("ignoring message number " + + seqnum + " outside range " + messageCache.size()); + return null; + } + return messageCache.getMessageBySeqnum(seqnum); + } + + /** + * Get the message objects for the given sequence numbers. + * + * ASSERT: This method must be called only when holding the + * messageCacheLock + * + * @param seqnums the array of message sequence numbers + * @return the IMAPMessage objects + * @since JavaMail 1.5.3 + */ + protected IMAPMessage[] getMessagesBySeqNumbers(int[] seqnums) { + IMAPMessage[] msgs = new IMAPMessage[seqnums.length]; + int nulls = 0; + // Map seq-numbers into actual Messages. + for (int i = 0; i < seqnums.length; i++) { + msgs[i] = getMessageBySeqNumber(seqnums[i]); + if (msgs[i] == null) + nulls++; + } + if (nulls > 0) { // compress the array to remove the nulls + IMAPMessage[] nmsgs = new IMAPMessage[seqnums.length - nulls]; + for (int i = 0, j = 0; i < msgs.length; i++) { + if (msgs[i] != null) + nmsgs[j++] = msgs[i]; + } + msgs = nmsgs; + } + return msgs; + } + + private boolean isDirectory() { + return ((type & HOLDS_FOLDERS) != 0); + } +} + +/** + * An object that holds a Message object + * and reports its size and writes it to another OutputStream + * on demand. Used by appendMessages to avoid the need to + * buffer the entire message in memory in a single byte array + * before sending it to the server. + */ +class MessageLiteral implements Literal { + private Message msg; + private int msgSize = -1; + private byte[] buf; // the buffered message, if not null + + public MessageLiteral(Message msg, int maxsize) + throws MessagingException, IOException { + this.msg = msg; + // compute the size here so exceptions can be returned immediately + LengthCounter lc = new LengthCounter(maxsize); + OutputStream os = new CRLFOutputStream(lc); + msg.writeTo(os); + os.flush(); + msgSize = lc.getSize(); + buf = lc.getBytes(); + } + + @Override + public int size() { + return msgSize; + } + + @Override + public void writeTo(OutputStream os) throws IOException { + // the message should not change between the constructor and this call + try { + if (buf != null) + os.write(buf, 0, msgSize); + else { + os = new CRLFOutputStream(os); + msg.writeTo(os); + } + } catch (MessagingException mex) { + // exceptions here are bad, "should" never happen + throw new IOException("MessagingException while appending message: " + + mex); + } + } +} + +/** + * Count the number of bytes written to the stream. + * Also, save a copy of small messages to avoid having to process + * the data again. + */ +class LengthCounter extends OutputStream { + private int size = 0; + private byte[] buf; + private int maxsize; + + public LengthCounter(int maxsize) { + buf = new byte[8192]; + this.maxsize = maxsize; + } + + @Override + public void write(int b) { + int newsize = size + 1; + if (buf != null) { + if (newsize > maxsize && maxsize >= 0) { + buf = null; + } else if (newsize > buf.length) { + byte newbuf[] = new byte[Math.max(buf.length << 1, newsize)]; + System.arraycopy(buf, 0, newbuf, 0, size); + buf = newbuf; + buf[size] = (byte)b; + } else { + buf[size] = (byte)b; + } + } + size = newsize; + } + + @Override + public void write(byte b[], int off, int len) { + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) > b.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + int newsize = size + len; + if (buf != null) { + if (newsize > maxsize && maxsize >= 0) { + buf = null; + } else if (newsize > buf.length) { + byte newbuf[] = new byte[Math.max(buf.length << 1, newsize)]; + System.arraycopy(buf, 0, newbuf, 0, size); + buf = newbuf; + System.arraycopy(b, off, buf, size, len); + } else { + System.arraycopy(b, off, buf, size, len); + } + } + size = newsize; + } + + @Override + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + public int getSize() { + return size; + } + + public byte[] getBytes() { + return buf; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPInputStream.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPInputStream.java new file mode 100644 index 000000000..ad1f67a04 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPInputStream.java @@ -0,0 +1,283 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.io.*; +import javax.mail.*; +import com.sun.mail.imap.protocol.*; +import com.sun.mail.iap.*; +import com.sun.mail.util.FolderClosedIOException; +import com.sun.mail.util.MessageRemovedIOException; + +/** + * This class implements an IMAP data stream. + * + * @author John Mani + */ + +public class IMAPInputStream extends InputStream { + private IMAPMessage msg; // this message + private String section; // section-id + private int pos; // track the position within the IMAP datastream + private int blksize; // number of bytes to read in each FETCH request + private int max; // the total number of bytes in this section. + // -1 indicates unknown + private byte[] buf; // the buffer obtained from fetchBODY() + private int bufcount; // The index one greater than the index of the + // last valid byte in 'buf' + private int bufpos; // The current position within 'buf' + private boolean lastBuffer; // is this the last buffer of data? + private boolean peek; // peek instead of fetch? + private ByteArray readbuf; // reuse for each read + + // Allocate this much extra space in the read buffer to allow + // space for the FETCH response overhead + private static final int slop = 64; + + + /** + * Create an IMAPInputStream. + * + * @param msg the IMAPMessage the data will come from + * @param section the IMAP section/part identifier for the data + * @param max the number of bytes in this section + * @param peek peek instead of fetch? + */ + public IMAPInputStream(IMAPMessage msg, String section, int max, + boolean peek) { + this.msg = msg; + this.section = section; + this.max = max; + this.peek = peek; + pos = 0; + blksize = msg.getFetchBlockSize(); + } + + /** + * Do a NOOP to force any untagged EXPUNGE responses + * and then check if this message is expunged. + */ + private void forceCheckExpunged() + throws MessageRemovedIOException, FolderClosedIOException { + synchronized (msg.getMessageCacheLock()) { + try { + msg.getProtocol().noop(); + } catch (ConnectionException cex) { + throw new FolderClosedIOException(msg.getFolder(), + cex.getMessage()); + } catch (FolderClosedException fex) { + throw new FolderClosedIOException(fex.getFolder(), + fex.getMessage()); + } catch (ProtocolException pex) { + // ignore it + } + } + if (msg.isExpunged()) + throw new MessageRemovedIOException(); + } + + /** + * Fetch more data from the server. This method assumes that all + * data has already been read in, hence bufpos > bufcount. + */ + private void fill() throws IOException { + /* + * If we've read the last buffer, there's no more to read. + * If we know the total number of bytes available from this + * section, let's check if we have consumed that many bytes. + */ + if (lastBuffer || max != -1 && pos >= max) { + if (pos == 0) + checkSeen(); + readbuf = null; // XXX - return to pool? + return; // the caller of fill() will return -1. + } + + BODY b = null; + if (readbuf == null) + readbuf = new ByteArray(blksize + slop); + + ByteArray ba; + int cnt; + // Acquire MessageCacheLock, to freeze seqnum. + synchronized (msg.getMessageCacheLock()) { + try { + IMAPProtocol p = msg.getProtocol(); + + // Check whether this message is expunged + if (msg.isExpunged()) + throw new MessageRemovedIOException( + "No content for expunged message"); + + int seqnum = msg.getSequenceNumber(); + cnt = blksize; + if (max != -1 && pos + blksize > max) + cnt = max - pos; + if (peek) + b = p.peekBody(seqnum, section, pos, cnt, readbuf); + else + b = p.fetchBody(seqnum, section, pos, cnt, readbuf); + } catch (ProtocolException pex) { + forceCheckExpunged(); + throw new IOException(pex.getMessage()); + } catch (FolderClosedException fex) { + throw new FolderClosedIOException(fex.getFolder(), + fex.getMessage()); + } + + if (b == null || ((ba = b.getByteArray()) == null)) { + forceCheckExpunged(); + // nope, the server doesn't think it's expunged. + // can't tell the difference between the server returning NIL + // and some other error that caused null to be returned above, + // so we'll just assume it was empty content. + ba = new ByteArray(0); + } + } + + // make sure the SEEN flag is set after reading the first chunk + if (pos == 0) + checkSeen(); + + // setup new values .. + buf = ba.getBytes(); + bufpos = ba.getStart(); + int n = ba.getCount(); // will be zero, if all data has been + // consumed from the server. + // if we got less than we asked for, this is the last buffer of data + lastBuffer = n < cnt; + bufcount = bufpos + n; + pos += n; + } + + /** + * Reads the next byte of data from this buffered input stream. + * If no byte is available, the value -1 is returned. + */ + @Override + public synchronized int read() throws IOException { + if (bufpos >= bufcount) { + fill(); + if (bufpos >= bufcount) + return -1; // EOF + } + return buf[bufpos++] & 0xff; + } + + /** + * Reads up to len bytes of data from this + * input stream into the given buffer.

    + * + * Returns the total number of bytes read into the buffer, + * or -1 if there is no more data.

    + * + * Note that this method mimics the "weird !" semantics of + * BufferedInputStream in that the number of bytes actually + * returned may be less that the requested value. So callers + * of this routine should be aware of this and must check + * the return value to insure that they have obtained the + * requisite number of bytes. + */ + @Override + public synchronized int read(byte b[], int off, int len) + throws IOException { + + int avail = bufcount - bufpos; + if (avail <= 0) { + fill(); + avail = bufcount - bufpos; + if (avail <= 0) + return -1; // EOF + } + int cnt = (avail < len) ? avail : len; + System.arraycopy(buf, bufpos, b, off, cnt); + bufpos += cnt; + return cnt; + } + + /** + * Reads up to b.length bytes of data from this input + * stream into an array of bytes.

    + * + * Returns the total number of bytes read into the buffer, or + * -1 is there is no more data.

    + * + * Note that this method mimics the "weird !" semantics of + * BufferedInputStream in that the number of bytes actually + * returned may be less that the requested value. So callers + * of this routine should be aware of this and must check + * the return value to insure that they have obtained the + * requisite number of bytes. + */ + @Override + public int read(byte b[]) throws IOException { + return read(b, 0, b.length); + } + + /** + * Returns the number of bytes that can be read from this input + * stream without blocking. + */ + @Override + public synchronized int available() throws IOException { + return (bufcount - bufpos); + } + + /** + * Normally the SEEN flag will have been set by now, but if not, + * force it to be set (as long as the folder isn't open read-only + * and we're not peeking). + * And of course, if there's no folder (e.g., a nested message) + * don't do anything. + */ + private void checkSeen() { + if (peek) // if we're peeking, don't set the SEEN flag + return; + try { + Folder f = msg.getFolder(); + if (f != null && f.getMode() != Folder.READ_ONLY && + !msg.isSet(Flags.Flag.SEEN)) + msg.setFlag(Flags.Flag.SEEN, true); + } catch (MessagingException ex) { + // ignore it + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPMessage.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPMessage.java new file mode 100644 index 000000000..f3237adbe --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPMessage.java @@ -0,0 +1,1724 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.util.Date; +import java.io.*; +import java.util.*; + +import javax.mail.*; +import javax.mail.internet.*; +import javax.activation.*; + +import com.sun.mail.util.ReadableMime; +import com.sun.mail.iap.*; +import com.sun.mail.imap.protocol.*; + +/** + * This class implements an IMAPMessage object.

    + * + * An IMAPMessage object starts out as a light-weight object. It gets + * filled-in incrementally when a request is made for some item. Or + * when a prefetch is done using the FetchProfile.

    + * + * An IMAPMessage has a messageNumber and a sequenceNumber. The + * messageNumber is its index into its containing folder's messageCache. + * The sequenceNumber is its IMAP sequence-number. + * + * @author John Mani + * @author Bill Shannon + */ +/* + * The lock hierarchy is that the lock on the IMAPMessage object, if + * it's acquired at all, must be acquired before the message cache lock. + * The IMAPMessage lock protects the message flags, sort of. + * + * XXX - I'm not convinced that all fields of IMAPMessage are properly + * protected by locks. + */ + +public class IMAPMessage extends MimeMessage implements ReadableMime { + protected BODYSTRUCTURE bs; // BODYSTRUCTURE + protected ENVELOPE envelope; // ENVELOPE + + /** + * A map of the extension FETCH items. In addition to saving the + * data in this map, an entry in this map indicates that we *have* + * the data, and so it doesn't need to be fetched again. The map + * is created only when needed, to avoid significantly increasing + * the effective size of an IMAPMessage object. + * + * @since JavaMail 1.4.6 + */ + protected Map items; // Map + + private Date receivedDate; // INTERNALDATE + private long size = -1; // RFC822.SIZE + + private Boolean peek; // use BODY.PEEK when fetching content? + + // this message's IMAP UID + private volatile long uid = -1; + + // this message's IMAP MODSEQ - RFC 4551 CONDSTORE + private volatile long modseq = -1; + + // this message's IMAP sectionId (null for toplevel message, + // non-null for a nested message) + protected String sectionId; + + // processed values + private String type; // Content-Type (with params) + private String subject; // decoded (Unicode) subject + private String description; // decoded (Unicode) desc + + // Indicates that we've loaded *all* headers for this message + private volatile boolean headersLoaded = false; + + // Indicates that we've cached the body of this message + private volatile boolean bodyLoaded = false; + + /* Hashtable of names of headers we've loaded from the server. + * Used in isHeaderLoaded() and getHeaderLoaded() to keep track + * of those headers we've attempted to load from the server. We + * need this table of names to avoid multiple attempts at loading + * headers that don't exist for a particular message. + * + * Could this somehow be included in the InternetHeaders object ?? + */ + private Hashtable loadedHeaders + = new Hashtable<>(1); + + // This is our Envelope + static final String EnvelopeCmd = "ENVELOPE INTERNALDATE RFC822.SIZE"; + + /** + * Constructor. + * + * @param folder the folder containing this message + * @param msgnum the message sequence number + */ + protected IMAPMessage(IMAPFolder folder, int msgnum) { + super(folder, msgnum); + flags = null; + } + + /** + * Constructor, for use by IMAPNestedMessage. + * + * @param session the Session + */ + protected IMAPMessage(Session session) { + super(session); + } + + /** + * Get this message's folder's protocol connection. + * Throws FolderClosedException, if the protocol connection + * is not available. + * + * ASSERT: Must hold the messageCacheLock. + * + * @return the IMAPProtocol object for the containing folder + * @exception ProtocolException for protocol errors + * @exception FolderClosedException if the folder is closed + */ + protected IMAPProtocol getProtocol() + throws ProtocolException, FolderClosedException { + ((IMAPFolder)folder).waitIfIdle(); + IMAPProtocol p = ((IMAPFolder)folder).protocol; + if (p == null) + throw new FolderClosedException(folder); + else + return p; + } + + /* + * Is this an IMAP4 REV1 server? + */ + protected boolean isREV1() throws FolderClosedException { + // access the folder's protocol object without waiting + // for IDLE to complete + IMAPProtocol p = ((IMAPFolder)folder).protocol; + if (p == null) + throw new FolderClosedException(folder); + else + return p.isREV1(); + } + + /** + * Get the messageCacheLock, associated with this Message's + * Folder. + * + * @return the message cache lock object + */ + protected Object getMessageCacheLock() { + return ((IMAPFolder)folder).messageCacheLock; + } + + /** + * Get this message's IMAP sequence number. + * + * ASSERT: This method must be called only when holding the + * messageCacheLock. + * + * @return the message sequence number + */ + protected int getSequenceNumber() { + return ((IMAPFolder)folder).messageCache.seqnumOf(getMessageNumber()); + } + + /** + * Wrapper around the protected method Message.setMessageNumber() to + * make that method accessible to IMAPFolder. + */ + @Override + protected void setMessageNumber(int msgnum) { + super.setMessageNumber(msgnum); + } + + /** + * Return the UID for this message. + * Returns -1 if not known; use UIDFolder.getUID() in this case. + * + * @return the UID + * @see javax.mail.UIDFolder#getUID + */ + protected long getUID() { + return uid; + } + + protected void setUID(long uid) { + this.uid = uid; + } + + /** + * Return the modification sequence number (MODSEQ) for this message. + * Returns -1 if not known. + * + * @return the modification sequence number + * @exception MessagingException for failures + * @see "RFC 4551" + * @since JavaMail 1.5.1 + */ + public synchronized long getModSeq() throws MessagingException { + if (modseq != -1) + return modseq; + + synchronized (getMessageCacheLock()) { // Acquire Lock + try { + IMAPProtocol p = getProtocol(); + checkExpunged(); // insure that message is not expunged + MODSEQ ms = p.fetchMODSEQ(getSequenceNumber()); + + if (ms != null) + modseq = ms.modseq; + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + return modseq; + } + + long _getModSeq() { + return modseq; + } + + void setModSeq(long modseq) { + this.modseq = modseq; + } + + // expose to MessageCache + @Override + protected void setExpunged(boolean set) { + super.setExpunged(set); + } + + // Convenience routine + protected void checkExpunged() throws MessageRemovedException { + if (expunged) + throw new MessageRemovedException(); + } + + /** + * Do a NOOP to force any untagged EXPUNGE responses + * and then check if this message is expunged. + * + * @exception MessageRemovedException if the message has been removed + * @exception FolderClosedException if the folder has been closed + */ + protected void forceCheckExpunged() + throws MessageRemovedException, FolderClosedException { + synchronized (getMessageCacheLock()) { + try { + getProtocol().noop(); + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + // ignore it + } + } + if (expunged) + throw new MessageRemovedException(); + } + + // Return the block size for FETCH requests + // MUST be overridden by IMAPNestedMessage + protected int getFetchBlockSize() { + return ((IMAPStore)folder.getStore()).getFetchBlockSize(); + } + + // Should we ignore the size in the BODYSTRUCTURE? + // MUST be overridden by IMAPNestedMessage + protected boolean ignoreBodyStructureSize() { + return ((IMAPStore)folder.getStore()).ignoreBodyStructureSize(); + } + + /** + * Get the "From" attribute. + */ + @Override + public Address[] getFrom() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getFrom(); + loadEnvelope(); + InternetAddress[] a = envelope.from; + /* + * Per RFC 2822, the From header is required, and thus the IMAP + * spec also requires that it be present, but we know that in + * practice it is often missing. Some servers fill in the + * From field with the Sender field in this case, but at least + * Exchange 2007 does not. Use the same fallback strategy used + * by MimeMessage. + */ + if (a == null || a.length == 0) + a = envelope.sender; + return aaclone(a); + } + + @Override + public void setFrom(Address address) throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + @Override + public void addFrom(Address[] addresses) throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the "Sender" attribute. + */ + @Override + public Address getSender() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getSender(); + loadEnvelope(); + if (envelope.sender != null && envelope.sender.length > 0) + return (envelope.sender)[0]; // there can be only one sender + else + return null; + } + + + @Override + public void setSender(Address address) throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the desired Recipient type. + */ + @Override + public Address[] getRecipients(Message.RecipientType type) + throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getRecipients(type); + loadEnvelope(); + + if (type == Message.RecipientType.TO) + return aaclone(envelope.to); + else if (type == Message.RecipientType.CC) + return aaclone(envelope.cc); + else if (type == Message.RecipientType.BCC) + return aaclone(envelope.bcc); + else + return super.getRecipients(type); + } + + @Override + public void setRecipients(Message.RecipientType type, Address[] addresses) + throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + @Override + public void addRecipients(Message.RecipientType type, Address[] addresses) + throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the ReplyTo addresses. + */ + @Override + public Address[] getReplyTo() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getReplyTo(); + loadEnvelope(); + /* + * The IMAP spec requires that the Reply-To field never be + * null, but at least Exchange 2007 fails to fill it in in + * some cases. Use the same fallback strategy used by + * MimeMessage. + */ + if (envelope.replyTo == null || envelope.replyTo.length == 0) + return getFrom(); + return aaclone(envelope.replyTo); + } + + @Override + public void setReplyTo(Address[] addresses) throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the decoded subject. + */ + @Override + public String getSubject() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getSubject(); + + if (subject != null) // already cached ? + return subject; + + loadEnvelope(); + if (envelope.subject == null) // no subject + return null; + + // Cache and return the decoded value. + try { + // The server *should* unfold the value, but just in case it + // doesn't we unfold it here. + subject = + MimeUtility.decodeText(MimeUtility.unfold(envelope.subject)); + } catch (UnsupportedEncodingException ex) { + subject = envelope.subject; + } + + return subject; + } + + @Override + public void setSubject(String subject, String charset) + throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the SentDate. + */ + @Override + public Date getSentDate() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getSentDate(); + loadEnvelope(); + if (envelope.date == null) + return null; + else + return new Date(envelope.date.getTime()); + } + + @Override + public void setSentDate(Date d) throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the received date (INTERNALDATE). + */ + @Override + public Date getReceivedDate() throws MessagingException { + checkExpunged(); + if (receivedDate == null) + loadEnvelope(); // have to go to the server for this + if (receivedDate == null) + return null; + else + return new Date(receivedDate.getTime()); + } + + /** + * Get the message size.

    + * + * Note that this returns RFC822.SIZE. That is, it's the + * size of the whole message, header and body included. + * Note also that if the size of the message is greater than + * Integer.MAX_VALUE (2GB), this method returns Integer.MAX_VALUE. + */ + @Override + public int getSize() throws MessagingException { + checkExpunged(); + // if bodyLoaded, size is already set + if (size == -1) + loadEnvelope(); // XXX - could just fetch the size + if (size > Integer.MAX_VALUE) + return Integer.MAX_VALUE; // the best we can do... + else + return (int)size; + } + + /** + * Get the message size as a long.

    + * + * Suitable for messages that might be larger than 2GB. + * @return the message size as a long integer + * @exception MessagingException for failures + * @since JavaMail 1.6 + */ + public long getSizeLong() throws MessagingException { + checkExpunged(); + // if bodyLoaded, size is already set + if (size == -1) + loadEnvelope(); // XXX - could just fetch the size + return size; + } + + /** + * Get the total number of lines.

    + * + * Returns the "body_fld_lines" field from the + * BODYSTRUCTURE. Note that this field is available + * only for text/plain and message/rfc822 types + */ + @Override + public int getLineCount() throws MessagingException { + checkExpunged(); + // XXX - superclass doesn't implement this + loadBODYSTRUCTURE(); + return bs.lines; + } + + /** + * Get the content language. + */ + @Override + public String[] getContentLanguage() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getContentLanguage(); + loadBODYSTRUCTURE(); + if (bs.language != null) + return bs.language.clone(); + else + return null; + } + + @Override + public void setContentLanguage(String[] languages) + throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the In-Reply-To header. + * + * @return the In-Reply-To header + * @exception MessagingException for failures + * @since JavaMail 1.3.3 + */ + public String getInReplyTo() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getHeader("In-Reply-To", " "); + loadEnvelope(); + return envelope.inReplyTo; + } + + /** + * Get the Content-Type. + * + * Generate this header from the BODYSTRUCTURE. Append parameters + * as well. + */ + @Override + public synchronized String getContentType() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getContentType(); + + // If we haven't cached the type yet .. + if (type == null) { + loadBODYSTRUCTURE(); + // generate content-type from BODYSTRUCTURE + ContentType ct = new ContentType(bs.type, bs.subtype, bs.cParams); + type = ct.toString(); + } + return type; + } + + /** + * Get the Content-Disposition. + */ + @Override + public String getDisposition() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getDisposition(); + loadBODYSTRUCTURE(); + return bs.disposition; + } + + @Override + public void setDisposition(String disposition) throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the Content-Transfer-Encoding. + */ + @Override + public String getEncoding() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getEncoding(); + loadBODYSTRUCTURE(); + return bs.encoding; + } + + /** + * Get the Content-ID. + */ + @Override + public String getContentID() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getContentID(); + loadBODYSTRUCTURE(); + return bs.id; + } + + @Override + public void setContentID(String cid) throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the Content-MD5. + */ + @Override + public String getContentMD5() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getContentMD5(); + loadBODYSTRUCTURE(); + return bs.md5; + } + + @Override + public void setContentMD5(String md5) throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the decoded Content-Description. + */ + @Override + public String getDescription() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getDescription(); + + if (description != null) // cached value ? + return description; + + loadBODYSTRUCTURE(); + if (bs.description == null) + return null; + + try { + description = MimeUtility.decodeText(bs.description); + } catch (UnsupportedEncodingException ex) { + description = bs.description; + } + + return description; + } + + @Override + public void setDescription(String description, String charset) + throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get the Message-ID. + */ + @Override + public String getMessageID() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getMessageID(); + loadEnvelope(); + return envelope.messageId; + } + + /** + * Get the "filename" Disposition parameter. (Only available in + * IMAP4rev1). If thats not available, get the "name" ContentType + * parameter. + */ + @Override + public String getFileName() throws MessagingException { + checkExpunged(); + if (bodyLoaded) + return super.getFileName(); + + String filename = null; + loadBODYSTRUCTURE(); + + if (bs.dParams != null) + filename = bs.dParams.get("filename"); + if (filename == null && bs.cParams != null) + filename = bs.cParams.get("name"); + return filename; + } + + @Override + public void setFileName(String filename) throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get all the bytes for this message. Overrides getContentStream() + * in MimeMessage. This method is ultimately used by the DataHandler + * to obtain the input stream for this message. + * + * @see javax.mail.internet.MimeMessage#getContentStream + */ + @Override + protected InputStream getContentStream() throws MessagingException { + if (bodyLoaded) + return super.getContentStream(); + InputStream is = null; + boolean pk = getPeek(); // get before acquiring message cache lock + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(getMessageCacheLock()) { + try { + IMAPProtocol p = getProtocol(); + + // This message could be expunged when we were waiting + // to acquire the lock ... + checkExpunged(); + + if (p.isREV1() && (getFetchBlockSize() != -1)) // IMAP4rev1 + return new IMAPInputStream(this, toSection("TEXT"), + bs != null && !ignoreBodyStructureSize() ? + bs.size : -1, pk); + + if (p.isREV1()) { + BODY b; + if (pk) + b = p.peekBody(getSequenceNumber(), toSection("TEXT")); + else + b = p.fetchBody(getSequenceNumber(), toSection("TEXT")); + if (b != null) + is = b.getByteArrayInputStream(); + } else { + RFC822DATA rd = p.fetchRFC822(getSequenceNumber(), "TEXT"); + if (rd != null) + is = rd.getByteArrayInputStream(); + } + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + forceCheckExpunged(); + throw new MessagingException(pex.getMessage(), pex); + } + } + + if (is == null) { + forceCheckExpunged(); // may throw MessageRemovedException + // nope, the server doesn't think it's expunged. + // can't tell the difference between the server returning NIL + // and some other error that caused null to be returned above, + // so we'll just assume it was empty content. + is = new ByteArrayInputStream(new byte[0]); + } + return is; + } + + /** + * Get the DataHandler object for this message. + */ + @Override + public synchronized DataHandler getDataHandler() + throws MessagingException { + checkExpunged(); + + if (dh == null && !bodyLoaded) { + loadBODYSTRUCTURE(); + if (type == null) { // type not yet computed + // generate content-type from BODYSTRUCTURE + ContentType ct = new ContentType(bs.type, bs.subtype, + bs.cParams); + type = ct.toString(); + } + + /* Special-case Multipart and Nested content. All other + * cases are handled by the superclass. + */ + if (bs.isMulti()) + dh = new DataHandler( + new IMAPMultipartDataSource(this, bs.bodies, + sectionId, this) + ); + else if (bs.isNested() && isREV1() && bs.envelope != null) + /* Nested messages are handled specially only for + * IMAP4rev1. IMAP4 doesn't provide enough support to + * FETCH the components of nested messages + */ + dh = new DataHandler( + new IMAPNestedMessage(this, + bs.bodies[0], + bs.envelope, + sectionId == null ? "1" : sectionId + ".1"), + type + ); + } + + return super.getDataHandler(); + } + + @Override + public void setDataHandler(DataHandler content) + throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Return the MIME format stream corresponding to this message. + * + * @return the MIME format stream + * @since JavaMail 1.4.5 + */ + @Override + public InputStream getMimeStream() throws MessagingException { + // XXX - need an "if (bodyLoaded)" version + InputStream is = null; + boolean pk = getPeek(); // get before acquiring message cache lock + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(getMessageCacheLock()) { + try { + IMAPProtocol p = getProtocol(); + + checkExpunged(); // insure this message is not expunged + + if (p.isREV1() && (getFetchBlockSize() != -1)) // IMAP4rev1 + return new IMAPInputStream(this, sectionId, -1, pk); + + if (p.isREV1()) { + BODY b; + if (pk) + b = p.peekBody(getSequenceNumber(), sectionId); + else + b = p.fetchBody(getSequenceNumber(), sectionId); + if (b != null) + is = b.getByteArrayInputStream(); + } else { + RFC822DATA rd = p.fetchRFC822(getSequenceNumber(), null); + if (rd != null) + is = rd.getByteArrayInputStream(); + } + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + forceCheckExpunged(); + throw new MessagingException(pex.getMessage(), pex); + } + } + + if (is == null) { + forceCheckExpunged(); // may throw MessageRemovedException + // nope, the server doesn't think it's expunged. + // can't tell the difference between the server returning NIL + // and some other error that caused null to be returned above, + // so we'll just assume it was empty content. + is = new ByteArrayInputStream(new byte[0]); + } + return is; + } + + /** + * Write out the bytes into the given OutputStream. + */ + @Override + public void writeTo(OutputStream os) + throws IOException, MessagingException { + if (bodyLoaded) { + super.writeTo(os); + return; + } + InputStream is = getMimeStream(); + try { + // write out the bytes + byte[] bytes = new byte[16*1024]; + int count; + while ((count = is.read(bytes)) != -1) + os.write(bytes, 0, count); + } finally { + is.close(); + } + } + + /** + * Get the named header. + */ + @Override + public String[] getHeader(String name) throws MessagingException { + checkExpunged(); + + if (isHeaderLoaded(name)) // already loaded ? + return headers.getHeader(name); + + // Load this particular header + InputStream is = null; + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(getMessageCacheLock()) { + try { + IMAPProtocol p = getProtocol(); + + // This message could be expunged when we were waiting + // to acquire the lock ... + checkExpunged(); + + if (p.isREV1()) { + BODY b = p.peekBody(getSequenceNumber(), + toSection("HEADER.FIELDS (" + name + ")") + ); + if (b != null) + is = b.getByteArrayInputStream(); + } else { + RFC822DATA rd = p.fetchRFC822(getSequenceNumber(), + "HEADER.LINES (" + name + ")"); + if (rd != null) + is = rd.getByteArrayInputStream(); + } + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + forceCheckExpunged(); + throw new MessagingException(pex.getMessage(), pex); + } + } + + // if we get this far without "is" being set, something has gone + // wrong; prevent a later NullPointerException and return null here + if (is == null) + return null; + + if (headers == null) + headers = new InternetHeaders(); + headers.load(is); // load this header into the Headers object. + setHeaderLoaded(name); // Mark this header as loaded + + return headers.getHeader(name); + } + + /** + * Get the named header. + */ + @Override + public String getHeader(String name, String delimiter) + throws MessagingException { + checkExpunged(); + + // force the header to be loaded by invoking getHeader(name) + if (getHeader(name) == null) + return null; + return headers.getHeader(name, delimiter); + } + + @Override + public void setHeader(String name, String value) + throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + @Override + public void addHeader(String name, String value) + throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + @Override + public void removeHeader(String name) + throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get all headers. + */ + @Override + public Enumeration

    getAllHeaders() throws MessagingException { + checkExpunged(); + loadHeaders(); + return super.getAllHeaders(); + } + + /** + * Get matching headers. + */ + @Override + public Enumeration
    getMatchingHeaders(String[] names) + throws MessagingException { + checkExpunged(); + loadHeaders(); + return super.getMatchingHeaders(names); + } + + /** + * Get non-matching headers. + */ + @Override + public Enumeration
    getNonMatchingHeaders(String[] names) + throws MessagingException { + checkExpunged(); + loadHeaders(); + return super.getNonMatchingHeaders(names); + } + + @Override + public void addHeaderLine(String line) throws MessagingException { + throw new IllegalWriteException("IMAPMessage is read-only"); + } + + /** + * Get all header-lines. + */ + @Override + public Enumeration getAllHeaderLines() throws MessagingException { + checkExpunged(); + loadHeaders(); + return super.getAllHeaderLines(); + } + + /** + * Get all matching header-lines. + */ + @Override + public Enumeration getMatchingHeaderLines(String[] names) + throws MessagingException { + checkExpunged(); + loadHeaders(); + return super.getMatchingHeaderLines(names); + } + + /** + * Get all non-matching headerlines. + */ + @Override + public Enumeration getNonMatchingHeaderLines(String[] names) + throws MessagingException { + checkExpunged(); + loadHeaders(); + return super.getNonMatchingHeaderLines(names); + } + + /** + * Get the Flags for this message. + */ + @Override + public synchronized Flags getFlags() throws MessagingException { + checkExpunged(); + loadFlags(); + return super.getFlags(); + } + + /** + * Test if the given Flags are set in this message. + */ + @Override + public synchronized boolean isSet(Flags.Flag flag) + throws MessagingException { + checkExpunged(); + loadFlags(); + return super.isSet(flag); + } + + /** + * Set/Unset the given flags in this message. + */ + @Override + public synchronized void setFlags(Flags flag, boolean set) + throws MessagingException { + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(getMessageCacheLock()) { + try { + IMAPProtocol p = getProtocol(); + checkExpunged(); // Insure that this message is not expunged + p.storeFlags(getSequenceNumber(), flag, set); + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } + } + } + + /** + * Set whether or not to use the PEEK variant of FETCH when + * fetching message content. This overrides the default + * value from the "mail.imap.peek" property. + * + * @param peek the peek flag + * @since JavaMail 1.3.3 + */ + public synchronized void setPeek(boolean peek) { + this.peek = Boolean.valueOf(peek); + } + + /** + * Get whether or not to use the PEEK variant of FETCH when + * fetching message content. + * + * @return the peek flag + * @since JavaMail 1.3.3 + */ + public synchronized boolean getPeek() { + if (peek == null) + return ((IMAPStore)folder.getStore()).getPeek(); + else + return peek.booleanValue(); + } + + /** + * Invalidate cached header and envelope information for this + * message. Subsequent accesses of this information will + * cause it to be fetched from the server. + * + * @since JavaMail 1.3.3 + */ + public synchronized void invalidateHeaders() { + headersLoaded = false; + loadedHeaders.clear(); + headers = null; + envelope = null; + bs = null; + receivedDate = null; + size = -1; + type = null; + subject = null; + description = null; + flags = null; + content = null; + contentStream = null; + bodyLoaded = false; + } + + /** + * This class implements the test to be done on each + * message in the folder. The test is to check whether the + * message has already cached all the items requested in the + * FetchProfile. If any item is missing, the test succeeds and + * breaks out. + */ + public static class FetchProfileCondition implements Utility.Condition { + private boolean needEnvelope = false; + private boolean needFlags = false; + private boolean needBodyStructure = false; + private boolean needUID = false; + private boolean needHeaders = false; + private boolean needSize = false; + private boolean needMessage = false; + private boolean needRDate = false; + private String[] hdrs = null; + private Set need = new HashSet<>(); + + /** + * Create a FetchProfileCondition to determine if we need to fetch + * any of the information specified in the FetchProfile. + * + * @param fp the FetchProfile + * @param fitems the FETCH items + */ + @SuppressWarnings("deprecation") // for FetchProfile.Item.SIZE + public FetchProfileCondition(FetchProfile fp, FetchItem[] fitems) { + if (fp.contains(FetchProfile.Item.ENVELOPE)) + needEnvelope = true; + if (fp.contains(FetchProfile.Item.FLAGS)) + needFlags = true; + if (fp.contains(FetchProfile.Item.CONTENT_INFO)) + needBodyStructure = true; + if (fp.contains(FetchProfile.Item.SIZE)) + needSize = true; + if (fp.contains(UIDFolder.FetchProfileItem.UID)) + needUID = true; + if (fp.contains(IMAPFolder.FetchProfileItem.HEADERS)) + needHeaders = true; + if (fp.contains(IMAPFolder.FetchProfileItem.SIZE)) + needSize = true; + if (fp.contains(IMAPFolder.FetchProfileItem.MESSAGE)) + needMessage = true; + if (fp.contains(IMAPFolder.FetchProfileItem.INTERNALDATE)) + needRDate = true; + hdrs = fp.getHeaderNames(); + for (int i = 0; i < fitems.length; i++) { + if (fp.contains(fitems[i].getFetchProfileItem())) + need.add(fitems[i]); + } + } + + /** + * Return true if we NEED to fetch the requested information + * for the specified message. + */ + @Override + public boolean test(IMAPMessage m) { + if (needEnvelope && m._getEnvelope() == null && !m.bodyLoaded) + return true; // no envelope + if (needFlags && m._getFlags() == null) + return true; // no flags + if (needBodyStructure && m._getBodyStructure() == null && + !m.bodyLoaded) + return true; // no BODYSTRUCTURE + if (needUID && m.getUID() == -1) // no UID + return true; + if (needHeaders && !m.areHeadersLoaded()) // no headers + return true; + if (needSize && m.size == -1 && !m.bodyLoaded) // no size + return true; + if (needMessage && !m.bodyLoaded) // no message body + return true; + if (needRDate && m.receivedDate == null) // no received date + return true; + + // Is the desired header present ? + for (int i = 0; i < hdrs.length; i++) { + if (!m.isHeaderLoaded(hdrs[i])) + return true; // Nope, return + } + Iterator it = need.iterator(); + while (it.hasNext()) { + FetchItem fitem = it.next(); + if (m.items == null || m.items.get(fitem.getName()) == null) + return true; + } + + return false; + } + } + + /** + * Apply the data in the FETCH item to this message. + * + * ASSERT: Must hold the messageCacheLock. + * + * @param item the fetch item + * @param hdrs the headers we're asking for + * @param allHeaders load all headers? + * @return did we handle this fetch item? + * @exception MessagingException for failures + * @since JavaMail 1.4.6 + */ + protected boolean handleFetchItem(Item item, + String[] hdrs, boolean allHeaders) + throws MessagingException { + // Check for the FLAGS item + if (item instanceof Flags) + flags = (Flags)item; + // Check for ENVELOPE items + else if (item instanceof ENVELOPE) + envelope = (ENVELOPE)item; + else if (item instanceof INTERNALDATE) + receivedDate = ((INTERNALDATE)item).getDate(); + else if (item instanceof RFC822SIZE) + size = ((RFC822SIZE)item).size; + else if (item instanceof MODSEQ) + modseq = ((MODSEQ)item).modseq; + + // Check for the BODYSTRUCTURE item + else if (item instanceof BODYSTRUCTURE) + bs = (BODYSTRUCTURE)item; + // Check for the UID item + else if (item instanceof UID) { + UID u = (UID)item; + uid = u.uid; // set uid + // add entry into uid table + if (((IMAPFolder)folder).uidTable == null) + ((IMAPFolder) folder).uidTable + = new Hashtable<>(); + ((IMAPFolder)folder).uidTable.put(Long.valueOf(u.uid), this); + } + + // Check for header items + else if (item instanceof RFC822DATA || + item instanceof BODY) { + InputStream headerStream; + boolean isHeader; + if (item instanceof RFC822DATA) { // IMAP4 + headerStream = + ((RFC822DATA)item).getByteArrayInputStream(); + isHeader = ((RFC822DATA)item).isHeader(); + } else { // IMAP4rev1 + headerStream = + ((BODY)item).getByteArrayInputStream(); + isHeader = ((BODY)item).isHeader(); + } + + if (!isHeader) { + // load the entire message by using the superclass + // MimeMessage.parse method + // first, save the size of the message + try { + size = headerStream.available(); + } catch (IOException ex) { + // should never occur + } + parse(headerStream); + bodyLoaded = true; + setHeadersLoaded(true); + } else { + // Load the obtained headers. + InternetHeaders h = new InternetHeaders(); + // Some IMAP servers (e.g., gmx.net) return NIL + // instead of a string just containing a CR/LF + // when the header list is empty. + if (headerStream != null) + h.load(headerStream); + if (headers == null || allHeaders) + headers = h; + else { + /* + * This is really painful. A second fetch + * of the same headers (which might occur because + * a new header was added to the set requested) + * will return headers we already know about. + * In this case, only load the headers we haven't + * seen before to avoid adding duplicates of + * headers we already have. + * + * XXX - There's a race condition here if another + * thread is reading headers in the same message + * object, because InternetHeaders is not thread + * safe. + */ + Enumeration
    e = h.getAllHeaders(); + while (e.hasMoreElements()) { + Header he = e.nextElement(); + if (!isHeaderLoaded(he.getName())) + headers.addHeader( + he.getName(), he.getValue()); + } + } + + // if we asked for all headers, assume we got them + if (allHeaders) + setHeadersLoaded(true); + else { + // Mark all headers we asked for as 'loaded' + for (int k = 0; k < hdrs.length; k++) + setHeaderLoaded(hdrs[k]); + } + } + } else + return false; // not handled + return true; // something above handled it + } + + /** + * Apply the data in the extension FETCH items to this message. + * This method adds all the items to the items map. + * Subclasses may override this method to call super and then + * also copy the data to a more convenient form. + * + * ASSERT: Must hold the messageCacheLock. + * + * @param extensionItems the Map to add fetch items to + * @since JavaMail 1.4.6 + */ + protected void handleExtensionFetchItems( + Map extensionItems) { + if (extensionItems == null || extensionItems.isEmpty()) + return; + if (items == null) + items = new HashMap<>(); + items.putAll(extensionItems); + } + + /** + * Fetch an individual item for the current message. + * Note that handleExtensionFetchItems will have been called + * to store this item in the message before this method + * returns. + * + * @param fitem the FetchItem + * @return the data associated with the FetchItem + * @exception MessagingException for failures + * @since JavaMail 1.4.6 + */ + protected Object fetchItem(FetchItem fitem) + throws MessagingException { + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(getMessageCacheLock()) { + Object robj = null; + + try { + IMAPProtocol p = getProtocol(); + + checkExpunged(); // Insure that this message is not expunged + + int seqnum = getSequenceNumber(); + Response[] r = p.fetch(seqnum, fitem.getName()); + + for (int i = 0; i < r.length; i++) { + // If this response is NOT a FetchResponse or if it does + // not match our seqnum, skip. + if (r[i] == null || + !(r[i] instanceof FetchResponse) || + ((FetchResponse)r[i]).getNumber() != seqnum) + continue; + + FetchResponse f = (FetchResponse)r[i]; + handleExtensionFetchItems(f.getExtensionItems()); + if (items != null) { + Object o = items.get(fitem.getName()); + if (o != null) + robj = o; + } + } + + // ((IMAPFolder)folder).handleResponses(r); + p.notifyResponseHandlers(r); + p.handleResult(r[r.length - 1]); + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + forceCheckExpunged(); + throw new MessagingException(pex.getMessage(), pex); + } + return robj; + + } // Release MessageCacheLock + } + + /** + * Return the data associated with the FetchItem. + * If the data hasn't been fetched, call the fetchItem + * method to fetch it. Returns null if there is no + * data for the FetchItem. + * + * @param fitem the FetchItem + * @return the data associated with the FetchItem + * @exception MessagingException for failures + * @since JavaMail 1.4.6 + */ + public synchronized Object getItem(FetchItem fitem) + throws MessagingException { + Object item = items == null ? null : items.get(fitem.getName()); + if (item == null) + item = fetchItem(fitem); + return item; + } + + /* + * Load the Envelope for this message. + */ + private synchronized void loadEnvelope() throws MessagingException { + if (envelope != null) // already loaded + return; + + Response[] r = null; + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(getMessageCacheLock()) { + try { + IMAPProtocol p = getProtocol(); + + checkExpunged(); // Insure that this message is not expunged + + int seqnum = getSequenceNumber(); + r = p.fetch(seqnum, EnvelopeCmd); + + for (int i = 0; i < r.length; i++) { + // If this response is NOT a FetchResponse or if it does + // not match our seqnum, skip. + if (r[i] == null || + !(r[i] instanceof FetchResponse) || + ((FetchResponse)r[i]).getNumber() != seqnum) + continue; + + FetchResponse f = (FetchResponse)r[i]; + + // Look for the Envelope items. + int count = f.getItemCount(); + for (int j = 0; j < count; j++) { + Item item = f.getItem(j); + + if (item instanceof ENVELOPE) + envelope = (ENVELOPE)item; + else if (item instanceof INTERNALDATE) + receivedDate = ((INTERNALDATE)item).getDate(); + else if (item instanceof RFC822SIZE) + size = ((RFC822SIZE)item).size; + } + } + + // ((IMAPFolder)folder).handleResponses(r); + p.notifyResponseHandlers(r); + p.handleResult(r[r.length - 1]); + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + forceCheckExpunged(); + throw new MessagingException(pex.getMessage(), pex); + } + + } // Release MessageCacheLock + + if (envelope == null) + throw new MessagingException("Failed to load IMAP envelope"); + } + + /* + * Load the BODYSTRUCTURE + */ + private synchronized void loadBODYSTRUCTURE() + throws MessagingException { + if (bs != null) // already loaded + return; + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(getMessageCacheLock()) { + try { + IMAPProtocol p = getProtocol(); + + // This message could be expunged when we were waiting + // to acquire the lock ... + checkExpunged(); + + bs = p.fetchBodyStructure(getSequenceNumber()); + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + forceCheckExpunged(); + throw new MessagingException(pex.getMessage(), pex); + } + if (bs == null) { + // if the FETCH is successful, we should always get a + // BODYSTRUCTURE, but some servers fail to return it + // if the message has been expunged + forceCheckExpunged(); + throw new MessagingException("Unable to load BODYSTRUCTURE"); + } + } + } + + /* + * Load all headers. + */ + private synchronized void loadHeaders() throws MessagingException { + if (headersLoaded) + return; + + InputStream is = null; + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized (getMessageCacheLock()) { + try { + IMAPProtocol p = getProtocol(); + + // This message could be expunged when we were waiting + // to acquire the lock ... + checkExpunged(); + + if (p.isREV1()) { + BODY b = p.peekBody(getSequenceNumber(), + toSection("HEADER")); + if (b != null) + is = b.getByteArrayInputStream(); + } else { + RFC822DATA rd = p.fetchRFC822(getSequenceNumber(), + "HEADER"); + if (rd != null) + is = rd.getByteArrayInputStream(); + } + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + forceCheckExpunged(); + throw new MessagingException(pex.getMessage(), pex); + } + } // Release MessageCacheLock + + if (is == null) + throw new MessagingException("Cannot load header"); + headers = new InternetHeaders(is); + headersLoaded = true; + } + + /* + * Load this message's Flags + */ + private synchronized void loadFlags() throws MessagingException { + if (flags != null) + return; + + // Acquire MessageCacheLock, to freeze seqnum. + synchronized(getMessageCacheLock()) { + try { + IMAPProtocol p = getProtocol(); + + // This message could be expunged when we were waiting + // to acquire the lock ... + checkExpunged(); + + flags = p.fetchFlags(getSequenceNumber()); + // make sure flags is always set, even if server is broken + if (flags == null) + flags = new Flags(); + } catch (ConnectionException cex) { + throw new FolderClosedException(folder, cex.getMessage()); + } catch (ProtocolException pex) { + forceCheckExpunged(); + throw new MessagingException(pex.getMessage(), pex); + } + } // Release MessageCacheLock + } + + /* + * Are all headers loaded? + */ + private boolean areHeadersLoaded() { + return headersLoaded; + } + + /* + * Set whether all headers are loaded. + */ + private void setHeadersLoaded(boolean loaded) { + headersLoaded = loaded; + } + + /* + * Check if the given header was ever loaded from the server + */ + private boolean isHeaderLoaded(String name) { + if (headersLoaded) // All headers for this message have been loaded + return true; + + return loadedHeaders.containsKey(name.toUpperCase(Locale.ENGLISH)); + } + + /* + * Mark that the given headers have been loaded from the server. + */ + private void setHeaderLoaded(String name) { + loadedHeaders.put(name.toUpperCase(Locale.ENGLISH), name); + } + + /* + * Convert the given FETCH item identifier to the approriate + * section-string for this message. + */ + private String toSection(String what) { + if (sectionId == null) + return what; + else + return sectionId + "." + what; + } + + /* + * Clone an array of InternetAddresses. + */ + private InternetAddress[] aaclone(InternetAddress[] aa) { + if (aa == null) + return null; + else + return aa.clone(); + } + + private Flags _getFlags() { + return flags; + } + + private ENVELOPE _getEnvelope() { + return envelope; + } + + private BODYSTRUCTURE _getBodyStructure() { + return bs; + } + + /*********************************************************** + * accessor routines to make available certain private/protected + * fields to other classes in this package. + ***********************************************************/ + + /* + * Called by IMAPFolder. + * Must not be synchronized. + */ + void _setFlags(Flags flags) { + this.flags = flags; + } + + /* + * Called by IMAPNestedMessage. + */ + Session _getSession() { + return session; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPMultipartDataSource.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPMultipartDataSource.java new file mode 100644 index 000000000..d95c60c8e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPMultipartDataSource.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + + +import javax.mail.*; +import javax.mail.internet.*; + +import com.sun.mail.imap.protocol.*; +import java.util.ArrayList; +import java.util.List; + +/** + * This class + * + * @author John Mani + */ + +public class IMAPMultipartDataSource extends MimePartDataSource + implements MultipartDataSource { + private List parts; + + protected IMAPMultipartDataSource(MimePart part, BODYSTRUCTURE[] bs, + String sectionId, IMAPMessage msg) { + super(part); + + parts = new ArrayList<>(bs.length); + for (int i = 0; i < bs.length; i++) + parts.add( + new IMAPBodyPart(bs[i], + sectionId == null ? + Integer.toString(i+1) : + sectionId + "." + Integer.toString(i+1), + msg) + ); + } + + @Override + public int getCount() { + return parts.size(); + } + + @Override + public BodyPart getBodyPart(int index) throws MessagingException { + return parts.get(index); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPNestedMessage.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPNestedMessage.java new file mode 100644 index 000000000..2e2782f2b --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPNestedMessage.java @@ -0,0 +1,161 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.io.*; +import javax.mail.*; +import com.sun.mail.imap.protocol.*; +import com.sun.mail.iap.ProtocolException; + +/** + * This class implements a nested IMAP message + * + * @author John Mani + */ + +public class IMAPNestedMessage extends IMAPMessage { + private IMAPMessage msg; // the enclosure of this nested message + + /** + * Package private constructor.

    + * + * Note that nested messages have no containing folder, nor + * a message number. + */ + IMAPNestedMessage(IMAPMessage m, BODYSTRUCTURE b, ENVELOPE e, String sid) { + super(m._getSession()); + msg = m; + bs = b; + envelope = e; + sectionId = sid; + setPeek(m.getPeek()); + } + + /* + * Get the enclosing message's Protocol object. Overrides + * IMAPMessage.getProtocol(). + */ + @Override + protected IMAPProtocol getProtocol() + throws ProtocolException, FolderClosedException { + return msg.getProtocol(); + } + + /* + * Is this an IMAP4 REV1 server? + */ + @Override + protected boolean isREV1() throws FolderClosedException { + return msg.isREV1(); + } + + /* + * Get the enclosing message's messageCacheLock. Overrides + * IMAPMessage.getMessageCacheLock(). + */ + @Override + protected Object getMessageCacheLock() { + return msg.getMessageCacheLock(); + } + + /* + * Get the enclosing message's sequence number. Overrides + * IMAPMessage.getSequenceNumber(). + */ + @Override + protected int getSequenceNumber() { + return msg.getSequenceNumber(); + } + + /* + * Check whether the enclosing message is expunged. Overrides + * IMAPMessage.checkExpunged(). + */ + @Override + protected void checkExpunged() throws MessageRemovedException { + msg.checkExpunged(); + } + + /* + * Check whether the enclosing message is expunged. Overrides + * Message.isExpunged(). + */ + @Override + public boolean isExpunged() { + return msg.isExpunged(); + } + + /* + * Get the enclosing message's fetchBlockSize. + */ + @Override + protected int getFetchBlockSize() { + return msg.getFetchBlockSize(); + } + + /* + * Get the enclosing message's ignoreBodyStructureSize. + */ + @Override + protected boolean ignoreBodyStructureSize() { + return msg.ignoreBodyStructureSize(); + } + + /* + * IMAPMessage uses RFC822.SIZE. We use the "size" field from + * our BODYSTRUCTURE. + */ + @Override + public int getSize() throws MessagingException { + return bs.size; + } + + /* + * Disallow setting flags on nested messages + */ + @Override + public synchronized void setFlags(Flags flag, boolean set) + throws MessagingException { + // Cannot set FLAGS on a nested IMAP message + throw new MethodNotSupportedException( + "Cannot set flags on this nested message"); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPProvider.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPProvider.java new file mode 100644 index 000000000..30753fc13 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPProvider.java @@ -0,0 +1,53 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import javax.mail.Provider; + +/** + * The IMAP protocol provider. + */ +public class IMAPProvider extends Provider { + public IMAPProvider() { + super(Provider.Type.STORE, "imap", IMAPStore.class.getName(), + "Oracle", null); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPSSLProvider.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPSSLProvider.java new file mode 100644 index 000000000..80d012980 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPSSLProvider.java @@ -0,0 +1,53 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import javax.mail.Provider; + +/** + * The IMAP SSL protocol provider. + */ +public class IMAPSSLProvider extends Provider { + public IMAPSSLProvider() { + super(Provider.Type.STORE, "imaps", IMAPSSLStore.class.getName(), + "Oracle", null); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPSSLStore.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPSSLStore.java new file mode 100644 index 000000000..c8dc1857d --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPSSLStore.java @@ -0,0 +1,61 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import javax.mail.*; + +/** + * This class provides access to an IMAP message store over SSL. + */ + +public class IMAPSSLStore extends IMAPStore { + + /** + * Constructor that takes a Session object and a URLName that + * represents a specific IMAP server. + * + * @param session the Session + * @param url the URLName of this store + */ + public IMAPSSLStore(Session session, URLName url) { + super(session, url, "imaps", true); // call super constructor + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPStore.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPStore.java new file mode 100644 index 000000000..fcaab75b8 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IMAPStore.java @@ -0,0 +1,2234 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.lang.reflect.*; +import java.util.Vector; +import java.util.StringTokenizer; +import java.util.Locale; +import java.util.Properties; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Map; +import java.util.HashMap; +import java.util.logging.Level; + +import javax.mail.*; +import javax.mail.event.*; + +import com.sun.mail.iap.*; +import com.sun.mail.imap.protocol.*; +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.MailLogger; +import com.sun.mail.util.SocketConnectException; +import com.sun.mail.util.MailConnectException; +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides access to an IMAP message store.

    + * + * Applications that need to make use of IMAP-specific features may cast + * a Store object to an IMAPStore object and + * use the methods on this class. The {@link #getQuota getQuota} and + * {@link #setQuota setQuota} methods support the IMAP QUOTA extension. + * Refer to RFC 2087 + * for more information.

    + * + * The {@link #id id} method supports the IMAP ID extension; + * see RFC 2971. + * The fields ID_NAME, ID_VERSION, etc. represent the suggested field names + * in RFC 2971 section 3.3 and may be used as keys in the Map containing + * client values or server values.

    + * + * See the com.sun.mail.imap package + * documentation for further information on the IMAP protocol provider.

    + * + * WARNING: The APIs unique to this class should be + * considered EXPERIMENTAL. They may be changed in the + * future in ways that are incompatible with applications using the + * current APIs. + * + * @author John Mani + * @author Bill Shannon + * @author Jim Glennon + */ +/* + * This package is implemented over the "imap.protocol" package, which + * implements the protocol-level commands.

    + * + * A connected IMAPStore maintains a pool of IMAP protocol objects for + * use in communicating with the IMAP server. The IMAPStore will create + * the initial AUTHENTICATED connection and seed the pool with this + * connection. As folders are opened and new IMAP protocol objects are + * needed, the IMAPStore will provide them from the connection pool, + * or create them if none are available. When a folder is closed, + * its IMAP protocol object is returned to the connection pool if the + * pool is not over capacity. The pool size can be configured by setting + * the mail.imap.connectionpoolsize property.

    + * + * Note that all connections in the connection pool have their response + * handler set to be the Store. When the connection is removed from the + * pool for use by a folder, the response handler is removed and then set + * to either the Folder or to the special nonStoreResponseHandler, depending + * on how the connection is being used. This is probably excessive. + * Better would be for the Protocol object to support only a single + * response handler, which would be set before the connection is used + * and cleared when the connection is in the pool and can't be used.

    + * + * A mechanism is provided for timing out idle connection pool IMAP + * protocol objects. Timed out connections are closed and removed (pruned) + * from the connection pool. The time out interval can be configured via + * the mail.imap.connectionpooltimeout property.

    + * + * The connected IMAPStore object may or may not maintain a separate IMAP + * protocol object that provides the store a dedicated connection to the + * IMAP server. This is provided mainly for compatibility with previous + * implementations of JavaMail and is determined by the value of the + * mail.imap.separatestoreconnection property.

    + * + * An IMAPStore object provides closed IMAPFolder objects thru its list() + * and listSubscribed() methods. A closed IMAPFolder object acquires an + * IMAP protocol object from the store to communicate with the server. When + * the folder is opened, it gets its own protocol object and thus its own, + * separate connection to the server. The store maintains references to + * all 'open' folders. When a folder is/gets closed, the store removes + * it from its list. When the store is/gets closed, it closes all open + * folders in its list, thus cleaning up all open connections to the + * server.

    + * + * A mutex is used to control access to the connection pool resources. + * Any time any of these resources need to be accessed, the following + * convention should be followed: + * + * synchronized (pool) { // ACQUIRE LOCK + * // access connection pool resources + * } // RELEASE LOCK

    + * + * The locking relationship between the store and folders is that the + * store lock must be acquired before a folder lock. This is currently only + * applicable in the store's cleanup method. It's important that the + * connection pool lock is not held when calling into folder objects. + * The locking hierarchy is that a folder lock must be acquired before + * any connection pool operations are performed. You never need to hold + * all three locks, but if you hold more than one this is the order you + * have to acquire them in.

    + * + * That is: Store > Folder, Folder > pool, Store > pool

    + * + * The IMAPStore implements the ResponseHandler interface and listens to + * BYE or untagged OK-notification events from the server as a result of + * Store operations. IMAPFolder forwards notifications that result from + * Folder operations using the store connection; the IMAPStore ResponseHandler + * is not used directly in this case.

    + */ + +public class IMAPStore extends Store + implements QuotaAwareStore, ResponseHandler { + + /** + * A special event type for a StoreEvent to indicate an IMAP + * response, if the mail.imap.enableimapevents property is set. + */ + public static final int RESPONSE = 1000; + + public static final String ID_NAME = "name"; + public static final String ID_VERSION = "version"; + public static final String ID_OS = "os"; + public static final String ID_OS_VERSION = "os-version"; + public static final String ID_VENDOR = "vendor"; + public static final String ID_SUPPORT_URL = "support-url"; + public static final String ID_ADDRESS = "address"; + public static final String ID_DATE = "date"; + public static final String ID_COMMAND = "command"; + public static final String ID_ARGUMENTS = "arguments"; + public static final String ID_ENVIRONMENT = "environment"; + + protected final String name; // name of this protocol + protected final int defaultPort; // default IMAP port + protected final boolean isSSL; // use SSL? + + private final int blksize; // Block size for data requested + // in FETCH requests. Defaults to + // 16K + + private boolean ignoreSize; // ignore the size in BODYSTRUCTURE? + + private final int statusCacheTimeout; // cache Status for 1 second + + private final int appendBufferSize; // max size of msg buffered for append + + private final int minIdleTime; // minimum idle time + + private volatile int port = -1; // port to use + + // Auth info + protected String host; + protected String user; + protected String password; + protected String proxyAuthUser; + protected String authorizationID; + protected String saslRealm; + + private Namespaces namespaces; + + private boolean enableStartTLS = false; // enable STARTTLS + private boolean requireStartTLS = false; // require STARTTLS + private boolean usingSSL = false; // using SSL? + private boolean enableSASL = false; // enable SASL authentication + private String[] saslMechanisms; + private boolean forcePasswordRefresh = false; + // enable notification of IMAP responses + private boolean enableResponseEvents = false; + // enable notification of IMAP responses during IDLE + private boolean enableImapEvents = false; + private String guid; // for Yahoo! Mail IMAP + private boolean throwSearchException = false; + private boolean peek = false; + private boolean closeFoldersOnStoreFailure = true; + private boolean enableCompress = false; // enable COMPRESS=DEFLATE + private boolean finalizeCleanClose = false; + + /* + * This field is set in the Store's response handler if we see + * a BYE response. The releaseStore method checks this field + * and if set it cleans up the Store. Field is volatile because + * there's no lock we consistently hold while manipulating it. + * + * Because volatile doesn't really work before JDK 1.5, + * use a lock to protect these two fields. + */ + private volatile boolean connectionFailed = false; + private volatile boolean forceClose = false; + private final Object connectionFailedLock = new Object(); + + private boolean debugusername; // include username in debug output? + private boolean debugpassword; // include password in debug output? + protected MailLogger logger; // for debug output + + private boolean messageCacheDebug; + + // constructors for IMAPFolder class provided by user + private volatile Constructor folderConstructor = null; + private volatile Constructor folderConstructorLI = null; + + // Connection pool info + + static class ConnectionPool { + + // container for the pool's IMAP protocol objects + private Vector authenticatedConnections + = new Vector<>(); + + // vectore of open folders + private Vector folders; + + // is the store connection being used? + private boolean storeConnectionInUse = false; + + // the last time (in millis) the pool was checked for timed out + // connections + private long lastTimePruned; + + // flag to indicate whether there is a dedicated connection for + // store commands + private final boolean separateStoreConnection; + + // client timeout interval + private final long clientTimeoutInterval; + + // server timeout interval + private final long serverTimeoutInterval; + + // size of the connection pool + private final int poolSize; + + // interval for checking for timed out connections + private final long pruningInterval; + + // connection pool logger + private final MailLogger logger; + + /* + * The idleState field supports the IDLE command. + * Normally when executing an IMAP command we hold the + * store's lock. + * While executing the IDLE command we can't hold the + * lock or it would prevent other threads from + * entering Store methods even far enough to check whether + * an IDLE command is in progress. We need to check before + * issuing another command so that we can abort the IDLE + * command. + * + * The idleState field is protected by the store's lock. + * The RUNNING state is the normal state and means no IDLE + * command is in progress. The IDLE state means we've issued + * an IDLE command and are reading responses. The ABORTING + * state means we've sent the DONE continuation command and + * are waiting for the thread running the IDLE command to + * break out of its read loop. + * + * When an IDLE command is in progress, the thread calling + * the idle method will be reading from the IMAP connection + * while not holding the store's lock. + * It's obviously critical that no other thread try to send a + * command or read from the connection while in this state. + * However, other threads can send the DONE continuation + * command that will cause the server to break out of the IDLE + * loop and send the ending tag response to the IDLE command. + * The thread in the idle method that's reading the responses + * from the IDLE command will see this ending response and + * complete the idle method, setting the idleState field back + * to RUNNING, and notifying any threads waiting to use the + * connection. + * + * All uses of the IMAP connection (IMAPProtocol object) must + * be preceeded by a check to make sure an IDLE command is not + * running, and abort the IDLE command if necessary. This check + * is made while holding the connection pool lock. While + * waiting for the IDLE command to complete, these other threads + * will give up the connection pool lock. This check is done by + * the getStoreProtocol() method. + */ + private static final int RUNNING = 0; // not doing IDLE command + private static final int IDLE = 1; // IDLE command in effect + private static final int ABORTING = 2; // IDLE command aborting + private int idleState = RUNNING; + private IMAPProtocol idleProtocol; // protocol object when IDLE + + ConnectionPool(String name, MailLogger plogger, Session session) { + lastTimePruned = System.currentTimeMillis(); + Properties props = session.getProperties(); + + boolean debug = PropUtil.getBooleanProperty(props, + "mail." + name + ".connectionpool.debug", false); + logger = plogger.getSubLogger("connectionpool", + "DEBUG IMAP CP", debug); + + // check if the default connection pool size is overridden + int size = PropUtil.getIntProperty(props, + "mail." + name + ".connectionpoolsize", -1); + if (size > 0) { + poolSize = size; + if (logger.isLoggable(Level.CONFIG)) + logger.config("mail.imap.connectionpoolsize: " + poolSize); + } else + poolSize = 1; + + // check if the default client-side timeout value is overridden + int connectionPoolTimeout = PropUtil.getIntProperty(props, + "mail." + name + ".connectionpooltimeout", -1); + if (connectionPoolTimeout > 0) { + clientTimeoutInterval = connectionPoolTimeout; + if (logger.isLoggable(Level.CONFIG)) + logger.config("mail.imap.connectionpooltimeout: " + + clientTimeoutInterval); + } else + clientTimeoutInterval = 45 * 1000; // 45 seconds + + // check if the default server-side timeout value is overridden + int serverTimeout = PropUtil.getIntProperty(props, + "mail." + name + ".servertimeout", -1); + if (serverTimeout > 0) { + serverTimeoutInterval = serverTimeout; + if (logger.isLoggable(Level.CONFIG)) + logger.config("mail.imap.servertimeout: " + + serverTimeoutInterval); + } else + serverTimeoutInterval = 30 * 60 * 1000; // 30 minutes + + // check if the default server-side timeout value is overridden + int pruning = PropUtil.getIntProperty(props, + "mail." + name + ".pruninginterval", -1); + if (pruning > 0) { + pruningInterval = pruning; + if (logger.isLoggable(Level.CONFIG)) + logger.config("mail.imap.pruninginterval: " + + pruningInterval); + } else + pruningInterval = 60 * 1000; // 1 minute + + // check to see if we should use a separate (i.e. dedicated) + // store connection + separateStoreConnection = + PropUtil.getBooleanProperty(props, + "mail." + name + ".separatestoreconnection", false); + if (separateStoreConnection) + logger.config("dedicate a store connection"); + + } + } + + private final ConnectionPool pool; + + /** + * A special response handler for connections that are being used + * to perform operations on behalf of an object other than the Store. + * It DOESN'T cause the Store to be cleaned up if a BYE is seen. + * The BYE may be real or synthetic and in either case just indicates + * that the connection is dead. + */ + private ResponseHandler nonStoreResponseHandler = new ResponseHandler() { + @Override + public void handleResponse(Response r) { + // Any of these responses may have a response code. + if (r.isOK() || r.isNO() || r.isBAD() || r.isBYE()) + handleResponseCode(r); + if (r.isBYE()) + logger.fine("IMAPStore non-store connection dead"); + } + }; + + /** + * Constructor that takes a Session object and a URLName that + * represents a specific IMAP server. + * + * @param session the Session + * @param url the URLName of this store + */ + public IMAPStore(Session session, URLName url) { + this(session, url, "imap", false); + } + + /** + * Constructor used by this class and by IMAPSSLStore subclass. + * + * @param session the Session + * @param url the URLName of this store + * @param name the protocol name for this store + * @param isSSL use SSL? + */ + protected IMAPStore(Session session, URLName url, + String name, boolean isSSL) { + super(session, url); // call super constructor + Properties props = session.getProperties(); + + if (url != null) + name = url.getProtocol(); + this.name = name; + if (!isSSL) + isSSL = PropUtil.getBooleanProperty(props, + "mail." + name + ".ssl.enable", false); + if (isSSL) + this.defaultPort = 993; + else + this.defaultPort = 143; + this.isSSL = isSSL; + + debug = session.getDebug(); + debugusername = PropUtil.getBooleanProperty(props, + "mail.debug.auth.username", true); + debugpassword = PropUtil.getBooleanProperty(props, + "mail.debug.auth.password", false); + logger = new MailLogger(this.getClass(), + "DEBUG " + name.toUpperCase(Locale.ENGLISH), + session.getDebug(), session.getDebugOut()); + + boolean partialFetch = PropUtil.getBooleanProperty(props, + "mail." + name + ".partialfetch", true); + if (!partialFetch) { + blksize = -1; + logger.config("mail.imap.partialfetch: false"); + } else { + blksize = PropUtil.getIntProperty(props, + "mail." + name +".fetchsize", 1024 * 16); + if (logger.isLoggable(Level.CONFIG)) + logger.config("mail.imap.fetchsize: " + blksize); + } + + ignoreSize = PropUtil.getBooleanProperty(props, + "mail." + name +".ignorebodystructuresize", false); + if (logger.isLoggable(Level.CONFIG)) + logger.config("mail.imap.ignorebodystructuresize: " + ignoreSize); + + statusCacheTimeout = PropUtil.getIntProperty(props, + "mail." + name + ".statuscachetimeout", 1000); + if (logger.isLoggable(Level.CONFIG)) + logger.config("mail.imap.statuscachetimeout: " + + statusCacheTimeout); + + appendBufferSize = PropUtil.getIntProperty(props, + "mail." + name + ".appendbuffersize", -1); + if (logger.isLoggable(Level.CONFIG)) + logger.config("mail.imap.appendbuffersize: " + appendBufferSize); + + minIdleTime = PropUtil.getIntProperty(props, + "mail." + name + ".minidletime", 10); + if (logger.isLoggable(Level.CONFIG)) + logger.config("mail.imap.minidletime: " + minIdleTime); + + // check if we should do a PROXYAUTH login + String s = session.getProperty("mail." + name + ".proxyauth.user"); + if (s != null) { + proxyAuthUser = s; + if (logger.isLoggable(Level.CONFIG)) + logger.config("mail.imap.proxyauth.user: " + proxyAuthUser); + } + + // check if STARTTLS is enabled + enableStartTLS = PropUtil.getBooleanProperty(props, + "mail." + name + ".starttls.enable", false); + if (enableStartTLS) + logger.config("enable STARTTLS"); + + // check if STARTTLS is required + requireStartTLS = PropUtil.getBooleanProperty(props, + "mail." + name + ".starttls.required", false); + if (requireStartTLS) + logger.config("require STARTTLS"); + + // check if SASL is enabled + enableSASL = PropUtil.getBooleanProperty(props, + "mail." + name + ".sasl.enable", false); + if (enableSASL) + logger.config("enable SASL"); + + // check if SASL mechanisms are specified + if (enableSASL) { + s = session.getProperty("mail." + name + ".sasl.mechanisms"); + if (s != null && s.length() > 0) { + if (logger.isLoggable(Level.CONFIG)) + logger.config("SASL mechanisms allowed: " + s); + List v = new ArrayList<>(5); + StringTokenizer st = new StringTokenizer(s, " ,"); + while (st.hasMoreTokens()) { + String m = st.nextToken(); + if (m.length() > 0) + v.add(m); + } + saslMechanisms = new String[v.size()]; + v.toArray(saslMechanisms); + } + } + + // check if an authorization ID has been specified + s = session.getProperty("mail." + name + ".sasl.authorizationid"); + if (s != null) { + authorizationID = s; + logger.log(Level.CONFIG, "mail.imap.sasl.authorizationid: {0}", + authorizationID); + } + + // check if a SASL realm has been specified + s = session.getProperty("mail." + name + ".sasl.realm"); + if (s != null) { + saslRealm = s; + logger.log(Level.CONFIG, "mail.imap.sasl.realm: {0}", saslRealm); + } + + // check if forcePasswordRefresh is enabled + forcePasswordRefresh = PropUtil.getBooleanProperty(props, + "mail." + name + ".forcepasswordrefresh", false); + if (forcePasswordRefresh) + logger.config("enable forcePasswordRefresh"); + + // check if enableimapevents is enabled + enableResponseEvents = PropUtil.getBooleanProperty(props, + "mail." + name + ".enableresponseevents", false); + if (enableResponseEvents) + logger.config("enable IMAP response events"); + + // check if enableresponseevents is enabled + enableImapEvents = PropUtil.getBooleanProperty(props, + "mail." + name + ".enableimapevents", false); + if (enableImapEvents) + logger.config("enable IMAP IDLE events"); + + // check if message cache debugging set + messageCacheDebug = PropUtil.getBooleanProperty(props, + "mail." + name + ".messagecache.debug", false); + + guid = session.getProperty("mail." + name + ".yahoo.guid"); + if (guid != null) + logger.log(Level.CONFIG, "mail.imap.yahoo.guid: {0}", guid); + + // check if throwsearchexception is enabled + throwSearchException = PropUtil.getBooleanProperty(props, + "mail." + name + ".throwsearchexception", false); + if (throwSearchException) + logger.config("throw SearchException"); + + // check if peek is set + peek = PropUtil.getBooleanProperty(props, + "mail." + name + ".peek", false); + if (peek) + logger.config("peek"); + + // check if closeFoldersOnStoreFailure is set + closeFoldersOnStoreFailure = PropUtil.getBooleanProperty(props, + "mail." + name + ".closefoldersonstorefailure", true); + if (closeFoldersOnStoreFailure) + logger.config("closeFoldersOnStoreFailure"); + + // check if COMPRESS is enabled + enableCompress = PropUtil.getBooleanProperty(props, + "mail." + name + ".compress.enable", false); + if (enableCompress) + logger.config("enable COMPRESS"); + + // check if finalizeCleanClose is enabled + finalizeCleanClose = PropUtil.getBooleanProperty(props, + "mail." + name + ".finalizecleanclose", false); + if (finalizeCleanClose) + logger.config("close connection cleanly in finalize"); + + s = session.getProperty("mail." + name + ".folder.class"); + if (s != null) { + logger.log(Level.CONFIG, "IMAP: folder class: {0}", s); + try { + ClassLoader cl = this.getClass().getClassLoader(); + + // now load the class + Class folderClass = null; + try { + // First try the "application's" class loader. + // This should eventually be replaced by + // Thread.currentThread().getContextClassLoader(). + folderClass = Class.forName(s, false, cl); + } catch (ClassNotFoundException ex1) { + // That didn't work, now try the "system" class loader. + // (Need both of these because JDK 1.1 class loaders + // may not delegate to their parent class loader.) + folderClass = Class.forName(s); + } + + Class[] c = { String.class, char.class, IMAPStore.class, + Boolean.class }; + folderConstructor = folderClass.getConstructor(c); + Class[] c2 = { ListInfo.class, IMAPStore.class }; + folderConstructorLI = folderClass.getConstructor(c2); + } catch (Exception ex) { + logger.log(Level.CONFIG, + "IMAP: failed to load folder class", ex); + } + } + + pool = new ConnectionPool(name, logger, session); + } + + /** + * Implementation of protocolConnect(). Will create a connection + * to the server and authenticate the user using the mechanisms + * specified by various properties.

    + * + * The host, user, and password + * parameters must all be non-null. If the authentication mechanism + * being used does not require a password, an empty string or other + * suitable dummy password should be used. + */ + @Override + protected synchronized boolean + protocolConnect(String host, int pport, String user, String password) + throws MessagingException { + + IMAPProtocol protocol = null; + + // check for non-null values of host, password, user + if (host == null || password == null || user == null) { + if (logger.isLoggable(Level.FINE)) + logger.fine("protocolConnect returning false" + + ", host=" + host + + ", user=" + traceUser(user) + + ", password=" + tracePassword(password)); + return false; + } + + // set the port correctly + if (pport != -1) { + port = pport; + } else { + port = PropUtil.getIntProperty(session.getProperties(), + "mail." + name + ".port", port); + } + + // use the default if needed + if (port == -1) { + port = defaultPort; + } + + try { + boolean poolEmpty; + synchronized (pool) { + poolEmpty = pool.authenticatedConnections.isEmpty(); + } + + if (poolEmpty) { + if (logger.isLoggable(Level.FINE)) + logger.fine("trying to connect to host \"" + host + + "\", port " + port + ", isSSL " + isSSL); + protocol = newIMAPProtocol(host, port); + if (logger.isLoggable(Level.FINE)) + logger.fine("protocolConnect login" + + ", host=" + host + + ", user=" + traceUser(user) + + ", password=" + tracePassword(password)); + protocol.addResponseHandler(nonStoreResponseHandler); + login(protocol, user, password); + protocol.removeResponseHandler(nonStoreResponseHandler); + protocol.addResponseHandler(this); + + usingSSL = protocol.isSSL(); // in case anyone asks + + this.host = host; + this.user = user; + this.password = password; + + synchronized (pool) { + pool.authenticatedConnections.addElement(protocol); + } + } + } catch (IMAPReferralException ex) { + // login failure due to IMAP REFERRAL, close connection to server + if (protocol != null) + protocol.disconnect(); + protocol = null; + throw new ReferralException(ex.getUrl(), ex.getMessage()); + } catch (CommandFailedException cex) { + // login failure, close connection to server + if (protocol != null) + protocol.disconnect(); + protocol = null; + Response r = cex.getResponse(); + throw new AuthenticationFailedException( + r != null ? r.getRest() : cex.getMessage()); + } catch (ProtocolException pex) { // any other exception + // failure in login command, close connection to server + if (protocol != null) + protocol.disconnect(); + protocol = null; + throw new MessagingException(pex.getMessage(), pex); + } catch (SocketConnectException scex) { + throw new MailConnectException(scex); + } catch (IOException ioex) { + throw new MessagingException(ioex.getMessage(), ioex); + } + + return true; + } + + /** + * Create an IMAPProtocol object connected to the host and port. + * Subclasses of IMAPStore may override this method to return a + * subclass of IMAPProtocol that supports product-specific extensions. + * + * @param host the host name + * @param port the port number + * @return the new IMAPProtocol object + * @exception IOException for I/O errors + * @exception ProtocolException for protocol errors + * @since JavaMail 1.4.6 + */ + protected IMAPProtocol newIMAPProtocol(String host, int port) + throws IOException, ProtocolException { + return new IMAPProtocol(name, host, port, + session.getProperties(), + isSSL, + logger + ); + } + + private void login(IMAPProtocol p, String u, String pw) + throws ProtocolException { + // turn on TLS if it's been enabled or required and is supported + // and we're not already using SSL + if ((enableStartTLS || requireStartTLS) && !p.isSSL()) { + if (p.hasCapability("STARTTLS")) { + p.startTLS(); + // if startTLS succeeds, refresh capabilities + p.capability(); + } else if (requireStartTLS) { + logger.fine("STARTTLS required but not supported by server"); + throw new ProtocolException( + "STARTTLS required but not supported by server"); + } + } + if (p.isAuthenticated()) + return; // no need to login + + // allow subclasses to issue commands before login + preLogin(p); + + // issue special ID command to Yahoo! Mail IMAP server + // http://en.wikipedia.org/wiki/Yahoo%21_Mail#Free_IMAP_and_SMTPs_access + if (guid != null) { + Map gmap = new HashMap<>(); + gmap.put("GUID", guid); + p.id(gmap); + } + + /* + * Put a special "marker" in the capabilities list so we can + * detect if the server refreshed the capabilities in the OK + * response. + */ + p.getCapabilities().put("__PRELOGIN__", ""); + String authzid; + if (authorizationID != null) + authzid = authorizationID; + else if (proxyAuthUser != null) + authzid = proxyAuthUser; + else + authzid = null; + + if (enableSASL) { + try { + p.sasllogin(saslMechanisms, saslRealm, authzid, u, pw); + if (!p.isAuthenticated()) + throw new CommandFailedException( + "SASL authentication failed"); + } catch (UnsupportedOperationException ex) { + // continue to try other authentication methods below + } + } + + if (!p.isAuthenticated()) + authenticate(p, authzid, u, pw); + + if (proxyAuthUser != null) + p.proxyauth(proxyAuthUser); + + /* + * If marker is still there, capabilities haven't been refreshed, + * refresh them now. + */ + if (p.hasCapability("__PRELOGIN__")) { + try { + p.capability(); + } catch (ConnectionException cex) { + throw cex; // rethrow connection failures + // XXX - assume connection has been closed + } catch (ProtocolException pex) { + // ignore other exceptions that "should never happen" + } + } + + if (enableCompress) { + if (p.hasCapability("COMPRESS=DEFLATE")) { + p.compress(); + } + } + + // if server supports UTF-8, enable it for client use + // note that this is safe to enable even if mail.mime.allowutf8=false + if (p.hasCapability("UTF8=ACCEPT") || p.hasCapability("UTF8=ONLY")) + p.enable("UTF8=ACCEPT"); + } + + /** + * Authenticate using one of the non-SASL mechanisms. + * + * @param p the IMAPProtocol object + * @param authzid the authorization ID + * @param user the user name + * @param password the password + * @exception ProtocolException on failures + */ + private void authenticate(IMAPProtocol p, String authzid, + String user, String password) + throws ProtocolException { + // this list must match the "if" statements below + String defaultAuthenticationMechanisms = "PLAIN LOGIN NTLM XOAUTH2"; + + // setting mail.imap.auth.mechanisms controls which mechanisms will + // be used, and in what order they'll be considered. only the first + // match is used. + String mechs = session.getProperty("mail." + name + ".auth.mechanisms"); + + if (mechs == null) + mechs = defaultAuthenticationMechanisms; + + /* + * Loop through the list of mechanisms supplied by the user + * (or defaulted) and try each in turn. If the server supports + * the mechanism and we have an authenticator for the mechanism, + * and it hasn't been disabled, use it. + */ + StringTokenizer st = new StringTokenizer(mechs); + while (st.hasMoreTokens()) { + String m = st.nextToken(); + m = m.toUpperCase(Locale.ENGLISH); + + /* + * If using the default mechanisms, check if this one is disabled. + */ + if (mechs == defaultAuthenticationMechanisms) { + String dprop = "mail." + name + ".auth." + + m.toLowerCase(Locale.ENGLISH) + ".disable"; + boolean disabled = PropUtil.getBooleanProperty( + session.getProperties(), + dprop, m.equals("XOAUTH2")); + if (disabled) { + if (logger.isLoggable(Level.FINE)) + logger.fine("mechanism " + m + + " disabled by property: " + dprop); + continue; + } + } + + if (!(p.hasCapability("AUTH=" + m) || + (m.equals("LOGIN") && p.hasCapability("AUTH-LOGIN")))) { + logger.log(Level.FINE, "mechanism {0} not supported by server", + m); + continue; + } + + if (m.equals("PLAIN")) + p.authplain(authzid, user, password); + else if (m.equals("LOGIN")) + p.authlogin(user, password); + else if (m.equals("NTLM")) + p.authntlm(authzid, user, password); + else if (m.equals("XOAUTH2")) + p.authoauth2(user, password); + else { + logger.log(Level.FINE, "no authenticator for mechanism {0}", m); + continue; + } + return; + } + + if (!p.hasCapability("LOGINDISABLED")) { + p.login(user, password); + return; + } + + throw new ProtocolException("No login methods supported!"); + } + + /** + * This method is called after the connection is made and + * TLS is started (if needed), but before any authentication + * is attempted. Subclasses can override this method to + * issue commands that are needed in the "not authenticated" + * state. Note that if the connection is pre-authenticated, + * this method won't be called.

    + * + * The implementation of this method in this class does nothing. + * + * @param p the IMAPProtocol connection + * @exception ProtocolException for protocol errors + * @since JavaMail 1.4.4 + */ + protected void preLogin(IMAPProtocol p) throws ProtocolException { + } + + /** + * Does this IMAPStore use SSL when connecting to the server? + * + * @return true if using SSL + * @since JavaMail 1.4.6 + */ + public synchronized boolean isSSL() { + return usingSSL; + } + + /** + * Set the user name that will be used for subsequent connections + * after this Store is first connected (for example, when creating + * a connection to open a Folder). This value is overridden + * by any call to the Store's connect method.

    + * + * Some IMAP servers may provide an authentication ID that can + * be used for more efficient authentication for future connections. + * This authentication ID is provided in a server-specific manner + * not described here.

    + * + * Most applications will never need to use this method. + * + * @param user the user name for the store + * @since JavaMail 1.3.3 + */ + public synchronized void setUsername(String user) { + this.user = user; + } + + /** + * Set the password that will be used for subsequent connections + * after this Store is first connected (for example, when creating + * a connection to open a Folder). This value is overridden + * by any call to the Store's connect method.

    + * + * Most applications will never need to use this method. + * + * @param password the password for the store + * @since JavaMail 1.3.3 + */ + public synchronized void setPassword(String password) { + this.password = password; + } + + /* + * Get a new authenticated protocol object for this Folder. + * Also store a reference to this folder in our list of + * open folders. + */ + IMAPProtocol getProtocol(IMAPFolder folder) + throws MessagingException { + IMAPProtocol p = null; + + // keep looking for a connection until we get a good one + while (p == null) { + + // New authenticated protocol objects are either acquired + // from the connection pool, or created when the pool is + // empty or no connections are available. None are available + // if the current pool size is one and the separate store + // property is set or the connection is in use. + + synchronized (pool) { + + // If there's none available in the pool, + // create a new one. + if (pool.authenticatedConnections.isEmpty() || + (pool.authenticatedConnections.size() == 1 && + (pool.separateStoreConnection || pool.storeConnectionInUse))) { + + logger.fine("no connections in the pool, creating a new one"); + try { + if (forcePasswordRefresh) + refreshPassword(); + // Use cached host, port and timeout values. + p = newIMAPProtocol(host, port); + p.addResponseHandler(nonStoreResponseHandler); + // Use cached auth info + login(p, user, password); + p.removeResponseHandler(nonStoreResponseHandler); + } catch(Exception ex1) { + if (p != null) + try { + p.disconnect(); + } catch (Exception ex2) { } + p = null; + } + + if (p == null) + throw new MessagingException("connection failure"); + } else { + if (logger.isLoggable(Level.FINE)) + logger.fine("connection available -- size: " + + pool.authenticatedConnections.size()); + + // remove the available connection from the Authenticated queue + p = pool.authenticatedConnections.lastElement(); + pool.authenticatedConnections.removeElement(p); + + // check if the connection is still live + long lastUsed = System.currentTimeMillis() - p.getTimestamp(); + if (lastUsed > pool.serverTimeoutInterval) { + try { + /* + * Swap in a special response handler that will handle + * alerts, but won't cause the store to be closed and + * cleaned up if the connection is dead. + */ + p.removeResponseHandler(this); + p.addResponseHandler(nonStoreResponseHandler); + p.noop(); + p.removeResponseHandler(nonStoreResponseHandler); + p.addResponseHandler(this); + } catch (ProtocolException pex) { + try { + p.removeResponseHandler(nonStoreResponseHandler); + p.disconnect(); + } catch (RuntimeException ignored) { + // don't let any exception stop us + } + p = null; + continue; // try again, from the top + } + } + + // if proxyAuthUser has changed, switch to new user + if (proxyAuthUser != null && + !proxyAuthUser.equals(p.getProxyAuthUser()) && + p.hasCapability("X-UNAUTHENTICATE")) { + try { + /* + * Swap in a special response handler that will handle + * alerts, but won't cause the store to be closed and + * cleaned up if the connection is dead. + */ + p.removeResponseHandler(this); + p.addResponseHandler(nonStoreResponseHandler); + p.unauthenticate(); + login(p, user, password); + p.removeResponseHandler(nonStoreResponseHandler); + p.addResponseHandler(this); + } catch (ProtocolException pex) { + try { + p.removeResponseHandler(nonStoreResponseHandler); + p.disconnect(); + } catch (RuntimeException ignored) { + // don't let any exception stop us + } + p = null; + continue; // try again, from the top + } + } + + // remove the store as a response handler. + p.removeResponseHandler(this); + } + + // check if we need to look for client-side timeouts + timeoutConnections(); + + // Add folder to folder-list + if (folder != null) { + if (pool.folders == null) + pool.folders = new Vector<>(); + pool.folders.addElement(folder); + } + } + + } + + return p; + } + + /** + * Get this Store's protocol connection. + * + * When acquiring a store protocol object, it is important to + * use the following steps: + * + * IMAPProtocol p = null; + * try { + * p = getStoreProtocol(); + * // perform the command + * } catch (ConnectionException cex) { + * throw new StoreClosedException(this, cex.getMessage()); + * } catch (WhateverException ex) { + * // handle it + * } finally { + * releaseStoreProtocol(p); + * } + */ + private IMAPProtocol getStoreProtocol() throws ProtocolException { + IMAPProtocol p = null; + + while (p == null) { + synchronized (pool) { + waitIfIdle(); + + // If there's no authenticated connections available create a + // new one and place it in the authenticated queue. + if (pool.authenticatedConnections.isEmpty()) { + pool.logger.fine("getStoreProtocol() - no connections " + + "in the pool, creating a new one"); + try { + if (forcePasswordRefresh) + refreshPassword(); + // Use cached host, port and timeout values. + p = newIMAPProtocol(host, port); + // Use cached auth info + login(p, user, password); + } catch(Exception ex1) { + if (p != null) + try { + p.logout(); + } catch (Exception ex2) { } + p = null; + } + + if (p == null) + throw new ConnectionException( + "failed to create new store connection"); + + p.addResponseHandler(this); + pool.authenticatedConnections.addElement(p); + + } else { + // Always use the first element in the Authenticated queue. + if (pool.logger.isLoggable(Level.FINE)) + pool.logger.fine("getStoreProtocol() - " + + "connection available -- size: " + + pool.authenticatedConnections.size()); + p = pool.authenticatedConnections.firstElement(); + + // if proxyAuthUser has changed, switch to new user + if (proxyAuthUser != null && + !proxyAuthUser.equals(p.getProxyAuthUser()) && + p.hasCapability("X-UNAUTHENTICATE")) { + p.unauthenticate(); + login(p, user, password); + } + } + + if (pool.storeConnectionInUse) { + try { + // someone else is using the connection, give up + // and wait until they're done + p = null; + pool.wait(); + } catch (InterruptedException ex) { + // restore the interrupted state, which callers might + // depend on + Thread.currentThread().interrupt(); + // don't keep looking for a connection if we've been + // interrupted + throw new ProtocolException( + "Interrupted getStoreProtocol", ex); + } + } else { + pool.storeConnectionInUse = true; + + pool.logger.fine("getStoreProtocol() -- storeConnectionInUse"); + } + + timeoutConnections(); + } + } + return p; + } + + /** + * Get a store protocol object for use by a folder. + */ + IMAPProtocol getFolderStoreProtocol() throws ProtocolException { + IMAPProtocol p = getStoreProtocol(); + p.removeResponseHandler(this); + p.addResponseHandler(nonStoreResponseHandler); + return p; + } + + /* + * Some authentication systems use one time passwords + * or tokens, so each authentication request requires + * a new password. This "kludge" allows a callback + * to application code to get a new password. + * + * XXX - remove this when SASL support is added + */ + private void refreshPassword() { + if (logger.isLoggable(Level.FINE)) + logger.fine("refresh password, user: " + traceUser(user)); + InetAddress addr; + try { + addr = InetAddress.getByName(host); + } catch (UnknownHostException e) { + addr = null; + } + PasswordAuthentication pa = + session.requestPasswordAuthentication(addr, port, + name, null, user); + if (pa != null) { + user = pa.getUserName(); + password = pa.getPassword(); + } + } + + /** + * If a SELECT succeeds, but indicates that the folder is + * READ-ONLY, and the user asked to open the folder READ_WRITE, + * do we allow the open to succeed? + */ + boolean allowReadOnlySelect() { + return PropUtil.getBooleanProperty(session.getProperties(), + "mail." + name + ".allowreadonlyselect", false); + } + + /** + * Report whether the separateStoreConnection is set. + */ + boolean hasSeparateStoreConnection() { + return pool.separateStoreConnection; + } + + /** + * Return the connection pool logger. + */ + MailLogger getConnectionPoolLogger() { + return pool.logger; + } + + /** + * Report whether message cache debugging is enabled. + */ + boolean getMessageCacheDebug() { + return messageCacheDebug; + } + + /** + * Report whether the connection pool is full. + */ + boolean isConnectionPoolFull() { + + synchronized (pool) { + if (pool.logger.isLoggable(Level.FINE)) + pool.logger.fine("connection pool current size: " + + pool.authenticatedConnections.size() + + " pool size: " + pool.poolSize); + + return (pool.authenticatedConnections.size() >= pool.poolSize); + + } + } + + /** + * Release the protocol object back to the connection pool. + */ + void releaseProtocol(IMAPFolder folder, IMAPProtocol protocol) { + + synchronized (pool) { + if (protocol != null) { + // If the pool is not full, add the store as a response handler + // and return the protocol object to the connection pool. + if (!isConnectionPoolFull()) { + protocol.addResponseHandler(this); + pool.authenticatedConnections.addElement(protocol); + + if (logger.isLoggable(Level.FINE)) + logger.fine( + "added an Authenticated connection -- size: " + + pool.authenticatedConnections.size()); + } else { + logger.fine( + "pool is full, not adding an Authenticated connection"); + try { + protocol.logout(); + } catch (ProtocolException pex) {}; + } + } + + if (pool.folders != null) + pool.folders.removeElement(folder); + + timeoutConnections(); + } + } + + /** + * Release the store connection. + */ + private void releaseStoreProtocol(IMAPProtocol protocol) { + + // will be called from idle() without the Store lock held, + // but cleanup is synchronized and will acquire the Store lock + + if (protocol == null) { + cleanup(); // failed to ever get the connection + return; // nothing to release + } + + /* + * Read out the flag that says whether this connection failed + * before releasing the protocol object for others to use. + */ + boolean failed; + synchronized (connectionFailedLock) { + failed = connectionFailed; + connectionFailed = false; // reset for next use + } + + // now free the store connection + synchronized (pool) { + pool.storeConnectionInUse = false; + pool.notifyAll(); // in case anyone waiting + + pool.logger.fine("releaseStoreProtocol()"); + + timeoutConnections(); + } + + /* + * If the connection died while we were using it, clean up. + * It's critical that the store connection be freed and the + * connection pool not be locked while we do this. + */ + assert !Thread.holdsLock(pool); + if (failed) + cleanup(); + } + + /** + * Release a store protocol object that was being used by a folder. + */ + void releaseFolderStoreProtocol(IMAPProtocol protocol) { + if (protocol == null) + return; // should never happen + protocol.removeResponseHandler(nonStoreResponseHandler); + protocol.addResponseHandler(this); + synchronized (pool) { + pool.storeConnectionInUse = false; + pool.notifyAll(); // in case anyone waiting + + pool.logger.fine("releaseFolderStoreProtocol()"); + + timeoutConnections(); + } + } + + /** + * Empty the connection pool. + */ + private void emptyConnectionPool(boolean force) { + + synchronized (pool) { + for (int index = pool.authenticatedConnections.size() - 1; + index >= 0; --index) { + try { + IMAPProtocol p = + pool.authenticatedConnections.elementAt(index); + p.removeResponseHandler(this); + if (force) + p.disconnect(); + else + p.logout(); + } catch (ProtocolException pex) {}; + } + + pool.authenticatedConnections.removeAllElements(); + } + + pool.logger.fine("removed all authenticated connections from pool"); + } + + /** + * Check to see if it's time to shrink the connection pool. + */ + private void timeoutConnections() { + + synchronized (pool) { + + // If we've exceeded the pruning interval, look for stale + // connections to logout. + if (System.currentTimeMillis() - pool.lastTimePruned > + pool.pruningInterval && + pool.authenticatedConnections.size() > 1) { + + if (pool.logger.isLoggable(Level.FINE)) { + pool.logger.fine("checking for connections to prune: " + + (System.currentTimeMillis() - pool.lastTimePruned)); + pool.logger.fine("clientTimeoutInterval: " + + pool.clientTimeoutInterval); + } + + IMAPProtocol p; + + // Check the timestamp of the protocol objects in the pool and + // logout if the interval exceeds the client timeout value + // (leave the first connection). + for (int index = pool.authenticatedConnections.size() - 1; + index > 0; index--) { + p = pool.authenticatedConnections. + elementAt(index); + if (pool.logger.isLoggable(Level.FINE)) + pool.logger.fine("protocol last used: " + + (System.currentTimeMillis() - p.getTimestamp())); + if (System.currentTimeMillis() - p.getTimestamp() > + pool.clientTimeoutInterval) { + + pool.logger.fine( + "authenticated connection timed out, " + + "logging out the connection"); + + p.removeResponseHandler(this); + pool.authenticatedConnections.removeElementAt(index); + + try { + p.logout(); + } catch (ProtocolException pex) {} + } + } + pool.lastTimePruned = System.currentTimeMillis(); + } + } + } + + /** + * Get the block size to use for fetch requests on this Store. + */ + int getFetchBlockSize() { + return blksize; + } + + /** + * Ignore the size reported in the BODYSTRUCTURE when fetching data? + */ + boolean ignoreBodyStructureSize() { + return ignoreSize; + } + + /** + * Get a reference to the session. + */ + Session getSession() { + return session; + } + + /** + * Get the number of milliseconds to cache STATUS response. + */ + int getStatusCacheTimeout() { + return statusCacheTimeout; + } + + /** + * Get the maximum size of a message to buffer for append. + */ + int getAppendBufferSize() { + return appendBufferSize; + } + + /** + * Get the minimum amount of time to delay when returning from idle. + */ + int getMinIdleTime() { + return minIdleTime; + } + + /** + * Throw a SearchException if the search expression is too complex? + */ + boolean throwSearchException() { + return throwSearchException; + } + + /** + * Get the default "peek" value. + */ + boolean getPeek() { + return peek; + } + + /** + * Return true if the specified capability string is in the list + * of capabilities the server announced. + * + * @param capability the capability string + * @return true if the server supports this capability + * @exception MessagingException for failures + * @since JavaMail 1.3.3 + */ + public synchronized boolean hasCapability(String capability) + throws MessagingException { + IMAPProtocol p = null; + try { + p = getStoreProtocol(); + return p.hasCapability(capability); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + } + + /** + * Set the user name to be used with the PROXYAUTH command. + * The PROXYAUTH user name can also be set using the + * mail.imap.proxyauth.user property when this + * Store is created. + * + * @param user the user name to set + * @since JavaMail 1.5.1 + */ + public void setProxyAuthUser(String user) { + proxyAuthUser = user; + } + + /** + * Get the user name to be used with the PROXYAUTH command. + * + * @return the user name + * @since JavaMail 1.5.1 + */ + public String getProxyAuthUser() { + return proxyAuthUser; + } + + /** + * Check whether this store is connected. Override superclass + * method, to actually ping our server connection. + */ + @Override + public synchronized boolean isConnected() { + if (!super.isConnected()) { + // if we haven't been connected at all, don't bother with + // the NOOP. + return false; + } + + /* + * The below noop() request can: + * (1) succeed - in which case all is fine. + * + * (2) fail because the server returns NO or BAD, in which + * case we ignore it since we can't really do anything. + * (2) fail because a BYE response is obtained from the + * server + * (3) fail because the socket.write() to the server fails, + * in which case the iap.protocol() code converts the + * IOException into a BYE response. + * + * Thus, our BYE handler will take care of closing the Store + * in case our connection is really gone. + */ + + IMAPProtocol p = null; + try { + p = getStoreProtocol(); + p.noop(); + } catch (ProtocolException pex) { + // will return false below + } finally { + releaseStoreProtocol(p); + } + + + return super.isConnected(); + } + + /** + * Close this Store. + */ + @Override + public synchronized void close() throws MessagingException { + cleanup(); + // do these again in case cleanup returned early + // because we were already closed due to a failure + closeAllFolders(false); + emptyConnectionPool(false); + } + + @Override + protected void finalize() throws Throwable { + if (!finalizeCleanClose) { + // when finalizing, close connections abruptly + synchronized (connectionFailedLock) { + connectionFailed = true; + forceClose = true; + } + closeFoldersOnStoreFailure = true; // make sure folders get closed + } + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Cleanup before dying. + */ + private synchronized void cleanup() { + // if we're not connected, someone beat us to it + if (!super.isConnected()) { + logger.fine("IMAPStore cleanup, not connected"); + return; + } + + /* + * If forceClose is true, some thread ran into an error that suggests + * the server might be dead, so we force the folders to close + * abruptly without waiting for the server. Used when + * the store connection times out, for example. + */ + boolean force; + synchronized (connectionFailedLock) { + force = forceClose; + forceClose = false; + connectionFailed = false; + } + if (logger.isLoggable(Level.FINE)) + logger.fine("IMAPStore cleanup, force " + force); + + if (!force || closeFoldersOnStoreFailure) { + closeAllFolders(force); + } + + emptyConnectionPool(force); + + // to set the state and send the closed connection event + try { + super.close(); + } catch (MessagingException mex) { + // ignore it + } + logger.fine("IMAPStore cleanup done"); + } + + /** + * Close all open Folders. If force is true, close them forcibly. + */ + private void closeAllFolders(boolean force) { + List foldersCopy = null; + boolean done = true; + + // To avoid violating the locking hierarchy, there's no lock we + // can hold that prevents another thread from trying to open a + // folder at the same time we're trying to close all the folders. + // Thus, there's an inherent race condition here. We close all + // the folders we know about and then check whether any new folders + // have been opened in the mean time. We keep trying until we're + // successful in closing all the folders. + for (;;) { + // Make a copy of the folders list so we do not violate the + // folder-connection pool locking hierarchy. + synchronized (pool) { + if (pool.folders != null) { + done = false; + foldersCopy = pool.folders; + pool.folders = null; + } else { + done = true; + } + } + if (done) + break; + + // Close and remove any open folders under this Store. + for (int i = 0, fsize = foldersCopy.size(); i < fsize; i++) { + IMAPFolder f = foldersCopy.get(i); + + try { + if (force) { + logger.fine("force folder to close"); + // Don't want to wait for folder connection to timeout + // (if, for example, the server is down) so we close + // folders abruptly. + f.forceClose(); + } else { + logger.fine("close folder"); + f.close(false); + } + } catch (MessagingException mex) { + // Who cares ?! Ignore 'em. + } catch (IllegalStateException ex) { + // Ditto + } + } + + } + } + + /** + * Get the default folder, representing the root of this user's + * namespace. Returns a closed DefaultFolder object. + */ + @Override + public synchronized Folder getDefaultFolder() throws MessagingException { + checkConnected(); + return new DefaultFolder(this); + } + + /** + * Get named folder. Returns a new, closed IMAPFolder. + */ + @Override + public synchronized Folder getFolder(String name) + throws MessagingException { + checkConnected(); + return newIMAPFolder(name, IMAPFolder.UNKNOWN_SEPARATOR); + } + + /** + * Get named folder. Returns a new, closed IMAPFolder. + */ + @Override + public synchronized Folder getFolder(URLName url) + throws MessagingException { + checkConnected(); + return newIMAPFolder(url.getFile(), IMAPFolder.UNKNOWN_SEPARATOR); + } + + /** + * Create an IMAPFolder object. If user supplied their own class, + * use it. Otherwise, call the constructor. + * + * @param fullName the full name of the folder + * @param separator the separator character for the folder hierarchy + * @param isNamespace does this name represent a namespace? + * @return the new IMAPFolder object + */ + protected IMAPFolder newIMAPFolder(String fullName, char separator, + Boolean isNamespace) { + IMAPFolder f = null; + if (folderConstructor != null) { + try { + Object[] o = + { fullName, Character.valueOf(separator), this, isNamespace }; + f = (IMAPFolder)folderConstructor.newInstance(o); + } catch (Exception ex) { + logger.log(Level.FINE, + "exception creating IMAPFolder class", ex); + } + } + if (f == null) + f = new IMAPFolder(fullName, separator, this, isNamespace); + return f; + } + + /** + * Create an IMAPFolder object. Call the newIMAPFolder method + * above with a null isNamespace. + * + * @param fullName the full name of the folder + * @param separator the separator character for the folder hierarchy + * @return the new IMAPFolder object + */ + protected IMAPFolder newIMAPFolder(String fullName, char separator) { + return newIMAPFolder(fullName, separator, null); + } + + /** + * Create an IMAPFolder object. If user supplied their own class, + * use it. Otherwise, call the constructor. + * + * @param li the ListInfo for the folder + * @return the new IMAPFolder object + */ + protected IMAPFolder newIMAPFolder(ListInfo li) { + IMAPFolder f = null; + if (folderConstructorLI != null) { + try { + Object[] o = { li, this }; + f = (IMAPFolder)folderConstructorLI.newInstance(o); + } catch (Exception ex) { + logger.log(Level.FINE, + "exception creating IMAPFolder class LI", ex); + } + } + if (f == null) + f = new IMAPFolder(li, this); + return f; + } + + /** + * Using the IMAP NAMESPACE command (RFC 2342), return a set + * of folders representing the Personal namespaces. + */ + @Override + public Folder[] getPersonalNamespaces() throws MessagingException { + Namespaces ns = getNamespaces(); + if (ns == null || ns.personal == null) + return super.getPersonalNamespaces(); + return namespaceToFolders(ns.personal, null); + } + + /** + * Using the IMAP NAMESPACE command (RFC 2342), return a set + * of folders representing the User's namespaces. + */ + @Override + public Folder[] getUserNamespaces(String user) + throws MessagingException { + Namespaces ns = getNamespaces(); + if (ns == null || ns.otherUsers == null) + return super.getUserNamespaces(user); + return namespaceToFolders(ns.otherUsers, user); + } + + /** + * Using the IMAP NAMESPACE command (RFC 2342), return a set + * of folders representing the Shared namespaces. + */ + @Override + public Folder[] getSharedNamespaces() throws MessagingException { + Namespaces ns = getNamespaces(); + if (ns == null || ns.shared == null) + return super.getSharedNamespaces(); + return namespaceToFolders(ns.shared, null); + } + + private synchronized Namespaces getNamespaces() throws MessagingException { + checkConnected(); + + IMAPProtocol p = null; + + if (namespaces == null) { + try { + p = getStoreProtocol(); + namespaces = p.namespace(); + } catch (BadCommandException bex) { + // NAMESPACE not supported, ignore it + } catch (ConnectionException cex) { + throw new StoreClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + } + return namespaces; + } + + private Folder[] namespaceToFolders(Namespaces.Namespace[] ns, + String user) { + Folder[] fa = new Folder[ns.length]; + for (int i = 0; i < fa.length; i++) { + String name = ns[i].prefix; + if (user == null) { + // strip trailing delimiter + int len = name.length(); + if ( len > 0 && name.charAt(len - 1) == ns[i].delimiter) + name = name.substring(0, len - 1); + } else { + // add user + name += user; + } + fa[i] = newIMAPFolder(name, ns[i].delimiter, + Boolean.valueOf(user == null)); + } + return fa; + } + + /** + * Get the quotas for the named quota root. + * Quotas are controlled on the basis of a quota root, not + * (necessarily) a folder. The relationship between folders + * and quota roots depends on the IMAP server. Some servers + * might implement a single quota root for all folders owned by + * a user. Other servers might implement a separate quota root + * for each folder. A single folder can even have multiple + * quota roots, perhaps controlling quotas for different + * resources. + * + * @param root the name of the quota root + * @return array of Quota objects + * @exception MessagingException if the server doesn't support the + * QUOTA extension + */ + @Override + public synchronized Quota[] getQuota(String root) + throws MessagingException { + checkConnected(); + Quota[] qa = null; + + IMAPProtocol p = null; + try { + p = getStoreProtocol(); + qa = p.getQuotaRoot(root); + } catch (BadCommandException bex) { + throw new MessagingException("QUOTA not supported", bex); + } catch (ConnectionException cex) { + throw new StoreClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + return qa; + } + + /** + * Set the quotas for the quota root specified in the quota argument. + * Typically this will be one of the quota roots obtained from the + * getQuota method, but it need not be. + * + * @param quota the quota to set + * @exception MessagingException if the server doesn't support the + * QUOTA extension + */ + @Override + public synchronized void setQuota(Quota quota) throws MessagingException { + checkConnected(); + IMAPProtocol p = null; + try { + p = getStoreProtocol(); + p.setQuota(quota); + } catch (BadCommandException bex) { + throw new MessagingException("QUOTA not supported", bex); + } catch (ConnectionException cex) { + throw new StoreClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + } + + private void checkConnected() { + assert Thread.holdsLock(this); + if (!super.isConnected()) + throw new IllegalStateException("Not connected"); + } + + /** + * Response handler method. + */ + @Override + public void handleResponse(Response r) { + // Any of these responses may have a response code. + if (r.isOK() || r.isNO() || r.isBAD() || r.isBYE()) + handleResponseCode(r); + if (r.isBYE()) { + logger.fine("IMAPStore connection dead"); + // Store's IMAP connection is dead, save the response so that + // releaseStoreProtocol will cleanup later. + synchronized (connectionFailedLock) { + connectionFailed = true; + if (r.isSynthetic()) + forceClose = true; + } + return; + } + } + + /** + * Use the IMAP IDLE command (see + * RFC 2177), + * if supported by the server, to enter idle mode so that the server + * can send unsolicited notifications + * without the need for the client to constantly poll the server. + * Use a ConnectionListener to be notified of + * events. When another thread (e.g., the listener thread) + * needs to issue an IMAP comand for this Store, the idle mode will + * be terminated and this method will return. Typically the caller + * will invoke this method in a loop.

    + * + * If the mail.imap.enableimapevents property is set, notifications + * received while the IDLE command is active will be delivered to + * ConnectionListeners as events with a type of + * IMAPStore.RESPONSE. The event's message will be + * the raw IMAP response string. + * Note that most IMAP servers will not deliver any events when + * using the IDLE command on a connection with no mailbox selected + * (i.e., this method). In most cases you'll want to use the + * idle method on IMAPFolder.

    + * + * NOTE: This capability is highly experimental and likely will change + * in future releases.

    + * + * The mail.imap.minidletime property enforces a minimum delay + * before returning from this method, to ensure that other threads + * have a chance to issue commands before the caller invokes this + * method again. The default delay is 10 milliseconds. + * + * @exception MessagingException if the server doesn't support the + * IDLE extension + * @exception IllegalStateException if the store isn't connected + * + * @since JavaMail 1.4.1 + */ + public void idle() throws MessagingException { + IMAPProtocol p = null; + // ASSERT: Must NOT be called with the connection pool + // synchronization lock held. + assert !Thread.holdsLock(pool); + synchronized (this) { + checkConnected(); + } + boolean needNotification = false; + try { + synchronized (pool) { + p = getStoreProtocol(); + if (pool.idleState != ConnectionPool.RUNNING) { + // some other thread must be running the IDLE + // command, we'll just wait for it to finish + // without aborting it ourselves + try { + // give up lock and wait to be not idle + pool.wait(); + } catch (InterruptedException ex) { + // restore the interrupted state, which callers might + // depend on + Thread.currentThread().interrupt(); + // stop waiting and return to caller + throw new MessagingException("idle interrupted", ex); + } + return; + } + p.idleStart(); + needNotification = true; + pool.idleState = ConnectionPool.IDLE; + pool.idleProtocol = p; + } + + /* + * We gave up the pool lock so that other threads + * can get into the pool far enough to see that we're + * in IDLE and abort the IDLE. + * + * Now we read responses from the IDLE command, especially + * including unsolicited notifications from the server. + * We don't hold the pool lock while reading because + * it protects the idleState and other threads need to be + * able to examine the state. + * + * We hold the pool lock while processing the responses. + */ + for (;;) { + Response r = p.readIdleResponse(); + synchronized (pool) { + if (r == null || !p.processIdleResponse(r)) { + pool.idleState = ConnectionPool.RUNNING; + pool.idleProtocol = null; + pool.notifyAll(); + needNotification = false; + break; + } + } + if (enableImapEvents && r.isUnTagged()) { + notifyStoreListeners(IMAPStore.RESPONSE, r.toString()); + } + } + + /* + * Enforce a minimum delay to give time to threads + * processing the responses that came in while we + * were idle. + */ + int minidle = getMinIdleTime(); + if (minidle > 0) { + try { + Thread.sleep(minidle); + } catch (InterruptedException ex) { + // restore the interrupted state, which callers might + // depend on + Thread.currentThread().interrupt(); + } + } + + } catch (BadCommandException bex) { + throw new MessagingException("IDLE not supported", bex); + } catch (ConnectionException cex) { + throw new StoreClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + if (needNotification) { + synchronized (pool) { + pool.idleState = ConnectionPool.RUNNING; + pool.idleProtocol = null; + pool.notifyAll(); + } + } + releaseStoreProtocol(p); + } + } + + /* + * If an IDLE command is in progress, abort it if necessary, + * and wait until it completes. + * ASSERT: Must be called with the pool's lock held. + */ + private void waitIfIdle() throws ProtocolException { + assert Thread.holdsLock(pool); + while (pool.idleState != ConnectionPool.RUNNING) { + if (pool.idleState == ConnectionPool.IDLE) { + pool.idleProtocol.idleAbort(); + pool.idleState = ConnectionPool.ABORTING; + } + try { + // give up lock and wait to be not idle + pool.wait(); + } catch (InterruptedException ex) { + // If someone is trying to interrupt us we can't keep going + // around the loop waiting for IDLE to complete, but we can't + // just return because callers expect the idleState to be + // RUNNING when we return. Throwing this exception seems + // like the best choice. + throw new ProtocolException("Interrupted waitIfIdle", ex); + } + } + } + + /** + * Send the IMAP ID command (if supported by the server) and return + * the result from the server. The ID command identfies the client + * to the server and returns information about the server to the client. + * See RFC 2971. + * The returned Map is unmodifiable. + * + * @param clientParams a Map of keys and values identifying the client + * @return a Map of keys and values identifying the server + * @exception MessagingException if the server doesn't support the + * ID extension + * @since JavaMail 1.5.1 + */ + public synchronized Map id(Map clientParams) + throws MessagingException { + checkConnected(); + Map serverParams = null; + + IMAPProtocol p = null; + try { + p = getStoreProtocol(); + serverParams = p.id(clientParams); + } catch (BadCommandException bex) { + throw new MessagingException("ID not supported", bex); + } catch (ConnectionException cex) { + throw new StoreClosedException(this, cex.getMessage()); + } catch (ProtocolException pex) { + throw new MessagingException(pex.getMessage(), pex); + } finally { + releaseStoreProtocol(p); + } + return serverParams; + } + + /** + * Handle notifications and alerts. + * Response must be an OK, NO, BAD, or BYE response. + */ + void handleResponseCode(Response r) { + if (enableResponseEvents) + notifyStoreListeners(IMAPStore.RESPONSE, r.toString()); + String s = r.getRest(); // get the text after the response + boolean isAlert = false; + if (s.startsWith("[")) { // a response code + int i = s.indexOf(']'); + // remember if it's an alert + if (i > 0 && s.substring(0, i + 1).equalsIgnoreCase("[ALERT]")) + isAlert = true; + // strip off the response code in any event + s = s.substring(i + 1).trim(); + } + if (isAlert) + notifyStoreListeners(StoreEvent.ALERT, s); + else if (r.isUnTagged() && s.length() > 0) + // Only send notifications that come with untagged + // responses, and only if there is actually some + // text there. + notifyStoreListeners(StoreEvent.NOTICE, s); + } + + private String traceUser(String user) { + return debugusername ? user : ""; + } + + private String tracePassword(String password) { + return debugpassword ? password : + (password == null ? "" : ""); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/IdleManager.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/IdleManager.java new file mode 100644 index 000000000..0373b496d --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/IdleManager.java @@ -0,0 +1,515 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2014-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.Socket; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; +import java.util.logging.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; + +import javax.mail.*; + +import com.sun.mail.imap.protocol.IMAPProtocol; +import com.sun.mail.util.MailLogger; + +/** + * IdleManager uses the optional IMAP IDLE command + * (RFC 2177) + * to watch multiple folders for new messages. + * IdleManager uses an Executor to execute tasks in separate threads. + * An Executor is typically provided by an ExecutorService. + * For example, for a Java SE application: + *

    + *	ExecutorService es = Executors.newCachedThreadPool();
    + *	final IdleManager idleManager = new IdleManager(session, es);
    + * 
    + * For a Java EE 7 application: + *
    + *	{@literal @}Resource
    + *	ManagedExecutorService es;
    + *	final IdleManager idleManager = new IdleManager(session, es);
    + * 
    + * To watch for new messages in a folder, open the folder, register a listener, + * and ask the IdleManager to watch the folder: + *
    + *	Folder folder = store.getFolder("INBOX");
    + *	folder.open(Folder.READ_WRITE);
    + *	folder.addMessageCountListener(new MessageCountAdapter() {
    + *	    public void messagesAdded(MessageCountEvent ev) {
    + *		Folder folder = (Folder)ev.getSource();
    + *		Message[] msgs = ev.getMessages();
    + *		System.out.println("Folder: " + folder +
    + *		    " got " + msgs.length + " new messages");
    + *		try {
    + *		    // process new messages
    + *		    idleManager.watch(folder); // keep watching for new messages
    + *		} catch (MessagingException mex) {
    + *		    // handle exception related to the Folder
    + *		}
    + *	    }
    + *	});
    + *	idleManager.watch(folder);
    + * 
    + * This delivers the events for each folder in a separate thread, NOT + * using the Executor. To deliver all events in a single thread + * using the Executor, set the following properties for the Session + * (once), and then add listeners and watch the folder as above. + *
    + *	// the following should be done once...
    + *	Properties props = session.getProperties();
    + *	props.put("mail.event.scope", "session"); // or "application"
    + *	props.put("mail.event.executor", es);
    + * 
    + * Note that, after processing new messages in your listener, or doing any + * other operations on the folder in any other thread, you need to tell + * the IdleManager to watch for more new messages. Unless, of course, you + * close the folder. + *

    + * The IdleManager is created with a Session, which it uses only to control + * debug output. A single IdleManager instance can watch multiple Folders + * from multiple Stores and multiple Sessions. + *

    + * Due to limitations in the Java SE nio support, a + * {@link java.nio.channels.SocketChannel SocketChannel} must be used instead + * of a {@link java.net.Socket Socket} to connect to the server. However, + * SocketChannels don't support all the features of Sockets, such as connecting + * through a SOCKS proxy server. SocketChannels also don't support + * simultaneous read and write, which means that the + * {@link com.sun.mail.imap.IMAPFolder#idle idle} method can't be used if + * SocketChannels are being used; use this IdleManager instead. + * To enable support for SocketChannels instead of Sockets, set the + * mail.imap.usesocketchannels property in the Session used to + * access the IMAP Folder. (Or mail.imaps.usesocketchannels if + * you're using the "imaps" protocol.) This will effect all connections in + * that Session, but you can create another Session without this property set + * if you need to use the features that are incompatible with SocketChannels. + *

    + * NOTE: The IdleManager, and all APIs and properties related to it, should + * be considered EXPERIMENTAL. They may be changed in the + * future in ways that are incompatible with applications using the + * current APIs. + * + * @since JavaMail 1.5.2 + */ +public class IdleManager { + private Executor es; + private Selector selector; + private MailLogger logger; + private volatile boolean die = false; + private volatile boolean running; + private Queue toWatch = new ConcurrentLinkedQueue<>(); + private Queue toAbort = new ConcurrentLinkedQueue<>(); + + /** + * Create an IdleManager. The Session is used only to configure + * debugging output. The Executor is used to create the + * "select" thread. + * + * @param session the Session containing configuration information + * @param es the Executor used to create threads + * @exception IOException for Selector failures + */ + public IdleManager(Session session, Executor es) throws IOException { + this.es = es; + logger = new MailLogger(this.getClass(), "DEBUG IMAP", + session.getDebug(), session.getDebugOut()); + selector = Selector.open(); + es.execute(new Runnable() { + @Override + public void run() { + logger.fine("IdleManager select starting"); + try { + running = true; + select(); + } finally { + running = false; + logger.fine("IdleManager select terminating"); + } + } + }); + } + + /** + * Is the IdleManager currently running? The IdleManager starts + * running when the Executor schedules its task. The IdleManager + * stops running after its task detects the stop request from the + * {@link #stop stop} method, or if it terminates abnormally due + * to an unexpected error. + * + * @return true if the IdleMaanger is running + * @since JavaMail 1.5.5 + */ + public boolean isRunning() { + return running; + } + + /** + * Watch the Folder for new messages and other events using the IMAP IDLE + * command. + * + * @param folder the folder to watch + * @exception MessagingException for errors related to the folder + */ + public void watch(Folder folder) + throws MessagingException { + if (die) // XXX - should be IllegalStateException? + throw new MessagingException("IdleManager is not running"); + if (!(folder instanceof IMAPFolder)) + throw new MessagingException("Can only watch IMAP folders"); + IMAPFolder ifolder = (IMAPFolder)folder; + SocketChannel sc = ifolder.getChannel(); + if (sc == null) { + if (folder.isOpen()) + throw new MessagingException( + "Folder is not using SocketChannels"); + else + throw new MessagingException("Folder is not open"); + } + if (logger.isLoggable(Level.FINEST)) + logger.log(Level.FINEST, "IdleManager watching {0}", + folderName(ifolder)); + // keep trying to start the IDLE command until we're successful. + // may block if we're in the middle of aborting an IDLE command. + int tries = 0; + while (!ifolder.startIdle(this)) { + if (logger.isLoggable(Level.FINEST)) + logger.log(Level.FINEST, + "IdleManager.watch startIdle failed for {0}", + folderName(ifolder)); + tries++; + } + if (logger.isLoggable(Level.FINEST)) { + if (tries > 0) + logger.log(Level.FINEST, + "IdleManager.watch startIdle succeeded for {0}" + + " after " + tries + " tries", + folderName(ifolder)); + else + logger.log(Level.FINEST, + "IdleManager.watch startIdle succeeded for {0}", + folderName(ifolder)); + } + synchronized (this) { + toWatch.add(ifolder); + selector.wakeup(); + } + } + + /** + * Request that the specified folder abort an IDLE command. + * We can't do the abort directly because the DONE message needs + * to be sent through the (potentially) SSL socket, which means + * we need to be in blocking I/O mode. We can only switch to + * blocking I/O mode when not selecting, so wake up the selector, + * which will process this request when it wakes up. + */ + void requestAbort(IMAPFolder folder) { + toAbort.add(folder); + selector.wakeup(); + } + + /** + * Run the {@link java.nio.channels.Selector#select select} loop + * to poll each watched folder for events sent from the server. + */ + private void select() { + die = false; + try { + while (!die) { + watchAll(); + logger.finest("IdleManager waiting..."); + int ns = selector.select(); + if (logger.isLoggable(Level.FINEST)) + logger.log(Level.FINEST, + "IdleManager selected {0} channels", ns); + if (die || Thread.currentThread().isInterrupted()) + break; + + /* + * Process any selected folders. We cancel the + * selection key for any selected folder, so if we + * need to continue watching that folder it's added + * to the toWatch list again. We can't actually + * register that folder again until the previous + * selection key is cancelled, so we call selectNow() + * just for the side effect of cancelling the selection + * keys. But if selectNow() selects something, we + * process it before adding folders from the toWatch + * queue. And so on until there is nothing to do, at + * which point it's safe to register folders from the + * toWatch queue. This should be "fair" since each + * selection key is used only once before being added + * to the toWatch list. + */ + do { + processKeys(); + } while (selector.selectNow() > 0 || !toAbort.isEmpty()); + } + } catch (InterruptedIOException ex) { + logger.log(Level.FINEST, "IdleManager interrupted", ex); + } catch (IOException ex) { + logger.log(Level.FINEST, "IdleManager got I/O exception", ex); + } catch (Exception ex) { + logger.log(Level.FINEST, "IdleManager got exception", ex); + } finally { + die = true; // prevent new watches in case of exception + logger.finest("IdleManager unwatchAll"); + try { + unwatchAll(); + selector.close(); + } catch (IOException ex2) { + // nothing to do... + logger.log(Level.FINEST, "IdleManager unwatch exception", ex2); + } + logger.fine("IdleManager exiting"); + } + } + + /** + * Register all of the folders in the queue with the selector, + * switching them to nonblocking I/O mode first. + */ + private void watchAll() { + /* + * Pull each of the folders from the toWatch queue + * and register it. + */ + IMAPFolder folder; + while ((folder = toWatch.poll()) != null) { + if (logger.isLoggable(Level.FINEST)) + logger.log(Level.FINEST, + "IdleManager adding {0} to selector", folderName(folder)); + try { + SocketChannel sc = folder.getChannel(); + if (sc == null) + continue; + // has to be non-blocking to select + sc.configureBlocking(false); + sc.register(selector, SelectionKey.OP_READ, folder); + } catch (IOException ex) { + // oh well, nothing to do + logger.log(Level.FINEST, + "IdleManager can't register folder", ex); + } catch (CancelledKeyException ex) { + // this should never happen + logger.log(Level.FINEST, + "IdleManager can't register folder", ex); + } + } + } + + /** + * Process the selected keys. + */ + private void processKeys() throws IOException { + IMAPFolder folder; + + /* + * First, process any channels with data to read. + */ + Set selectedKeys = selector.selectedKeys(); + /* + * XXX - this is simpler, but it can fail with + * ConcurrentModificationException + * + for (SelectionKey sk : selectedKeys) { + selectedKeys.remove(sk); // only process each key once + ... + } + */ + Iterator it = selectedKeys.iterator(); + while (it.hasNext()) { + SelectionKey sk = it.next(); + it.remove(); // only process each key once + // have to cancel so we can switch back to blocking I/O mode + sk.cancel(); + folder = (IMAPFolder)sk.attachment(); + if (logger.isLoggable(Level.FINEST)) + logger.log(Level.FINEST, + "IdleManager selected folder: {0}", folderName(folder)); + SelectableChannel sc = sk.channel(); + // switch back to blocking to allow normal I/O + sc.configureBlocking(true); + try { + if (folder.handleIdle(false)) { + if (logger.isLoggable(Level.FINEST)) + logger.log(Level.FINEST, + "IdleManager continue watching folder {0}", + folderName(folder)); + // more to do with this folder, select on it again + toWatch.add(folder); + } else { + // done watching this folder, + if (logger.isLoggable(Level.FINEST)) + logger.log(Level.FINEST, + "IdleManager done watching folder {0}", + folderName(folder)); + } + } catch (MessagingException ex) { + // something went wrong, stop watching this folder + logger.log(Level.FINEST, + "IdleManager got exception for folder: " + + folderName(folder), ex); + } + } + + /* + * Now, process any folders that we need to abort. + */ + while ((folder = toAbort.poll()) != null) { + if (logger.isLoggable(Level.FINEST)) + logger.log(Level.FINEST, + "IdleManager aborting IDLE for folder: {0}", + folderName(folder)); + SocketChannel sc = folder.getChannel(); + if (sc == null) + continue; + SelectionKey sk = sc.keyFor(selector); + // have to cancel so we can switch back to blocking I/O mode + if (sk != null) + sk.cancel(); + // switch back to blocking to allow normal I/O + sc.configureBlocking(true); + + // if there's a read timeout, have to do the abort in a new thread + Socket sock = sc.socket(); + if (sock != null && sock.getSoTimeout() > 0) { + logger.finest("IdleManager requesting DONE with timeout"); + toWatch.remove(folder); + final IMAPFolder folder0 = folder; + es.execute(new Runnable() { + @Override + public void run() { + // send the DONE and wait for the response + folder0.idleAbortWait(); + } + }); + } else { + folder.idleAbort(); // send the DONE message + // watch for OK response to DONE + // XXX - what if we also added it above? should be a nop + toWatch.add(folder); + } + } + } + + /** + * Stop watching all folders. Cancel any selection keys and, + * most importantly, switch the channel back to blocking mode. + * If there's any folders waiting to be watched, need to abort + * them too. + */ + private void unwatchAll() { + IMAPFolder folder; + Set keys = selector.keys(); + for (SelectionKey sk : keys) { + // have to cancel so we can switch back to blocking I/O mode + sk.cancel(); + folder = (IMAPFolder)sk.attachment(); + if (logger.isLoggable(Level.FINEST)) + logger.log(Level.FINEST, + "IdleManager no longer watching folder: {0}", + folderName(folder)); + SelectableChannel sc = sk.channel(); + // switch back to blocking to allow normal I/O + try { + sc.configureBlocking(true); + folder.idleAbortWait(); // send the DONE message and wait + } catch (IOException ex) { + // ignore it, channel might be closed + logger.log(Level.FINEST, + "IdleManager exception while aborting idle for folder: " + + folderName(folder), ex); + } + } + + /* + * Finally, process any folders waiting to be watched. + */ + while ((folder = toWatch.poll()) != null) { + if (logger.isLoggable(Level.FINEST)) + logger.log(Level.FINEST, + "IdleManager aborting IDLE for unwatched folder: {0}", + folderName(folder)); + SocketChannel sc = folder.getChannel(); + if (sc == null) + continue; + try { + // channel should still be in blocking mode, but make sure + sc.configureBlocking(true); + folder.idleAbortWait(); // send the DONE message and wait + } catch (IOException ex) { + // ignore it, channel might be closed + logger.log(Level.FINEST, + "IdleManager exception while aborting idle for folder: " + + folderName(folder), ex); + } + } + } + + /** + * Stop the IdleManager. The IdleManager can not be restarted. + */ + public synchronized void stop() { + die = true; + logger.fine("IdleManager stopping"); + selector.wakeup(); + } + + /** + * Return the fully qualified name of the folder, for use in log messages. + * Essentially just the getURLName method, but ignoring the + * MessagingException that can never happen. + */ + private static String folderName(Folder folder) { + try { + return folder.getURLName().toString(); + } catch (MessagingException mex) { + // can't happen + return folder.getStore().toString() + "/" + folder.toString(); + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/MessageCache.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/MessageCache.java new file mode 100644 index 000000000..bab6d8c9c --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/MessageCache.java @@ -0,0 +1,467 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.io.PrintStream; +import java.util.*; +import java.util.logging.Level; + +import javax.mail.*; +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.MailLogger; + +/** + * A cache of IMAPMessage objects along with the + * mapping from message number to IMAP sequence number. + * + * All operations on this object are protected by the messageCacheLock + * in IMAPFolder. + */ +public class MessageCache { + /* + * The array of IMAPMessage objects. Elements of the array might + * be null if no one has asked for the message. The array expands + * as needed and might be larger than the number of messages in the + * folder. The "size" field indicates the number of entries that + * are valid. + */ + private IMAPMessage[] messages; + + /* + * A parallel array of sequence numbers for each message. If the + * array pointer is null, the sequence number of a message is just + * its message number. This is the common case, until a message is + * expunged. + */ + private int[] seqnums; + + /* + * The amount of the messages (and seqnum) array that is valid. + * Might be less than the actual size of the array. + */ + private int size; + + /** + * The folder these messages belong to. + */ + private IMAPFolder folder; + + // debugging logger + private MailLogger logger; + + /** + * Grow the array by at least this much, to avoid constantly + * reallocating the array. + */ + private static final int SLOP = 64; + + /** + * Construct a new message cache of the indicated size. + */ + MessageCache(IMAPFolder folder, IMAPStore store, int size) { + this.folder = folder; + logger = folder.logger.getSubLogger("messagecache", "DEBUG IMAP MC", + store.getMessageCacheDebug()); + if (logger.isLoggable(Level.CONFIG)) + logger.config("create cache of size " + size); + ensureCapacity(size, 1); + } + + /** + * Constructor for debugging and testing. + */ + MessageCache(int size, boolean debug) { + this.folder = null; + logger = new MailLogger( + this.getClass(), "messagecache", + "DEBUG IMAP MC", debug, System.out); + if (logger.isLoggable(Level.CONFIG)) + logger.config("create DEBUG cache of size " + size); + ensureCapacity(size, 1); + } + + /** + * Size of cache. + * + * @return the size of the cache + */ + public int size() { + return size; + } + + /** + * Get the message object for the indicated message number. + * If the message object hasn't been created, create it. + * + * @param msgnum the message number + * @return the message + */ + public IMAPMessage getMessage(int msgnum) { + // check range + if (msgnum < 1 || msgnum > size) + throw new ArrayIndexOutOfBoundsException( + "message number (" + msgnum + ") out of bounds (" + size + ")"); + IMAPMessage msg = messages[msgnum-1]; + if (msg == null) { + if (logger.isLoggable(Level.FINE)) + logger.fine("create message number " + msgnum); + msg = folder.newIMAPMessage(msgnum); + messages[msgnum-1] = msg; + // mark message expunged if no seqnum + if (seqnumOf(msgnum) <= 0) { + logger.fine("it's expunged!"); + msg.setExpunged(true); + } + } + return msg; + } + + /** + * Get the message object for the indicated sequence number. + * If the message object hasn't been created, create it. + * Return null if there's no message with that sequence number. + * + * @param seqnum the sequence number of the message + * @return the message + */ + public IMAPMessage getMessageBySeqnum(int seqnum) { + int msgnum = msgnumOf(seqnum); + if (msgnum < 0) { // XXX - < 1 ? + if (logger.isLoggable(Level.FINE)) + logger.fine("no message seqnum " + seqnum); + return null; + } else + return getMessage(msgnum); + } + + /** + * Expunge the message with the given sequence number. + * + * @param seqnum the sequence number of the message to expunge + */ + public void expungeMessage(int seqnum) { + int msgnum = msgnumOf(seqnum); + if (msgnum < 0) { + if (logger.isLoggable(Level.FINE)) + logger.fine("expunge no seqnum " + seqnum); + return; // XXX - should never happen + } + IMAPMessage msg = messages[msgnum-1]; + if (msg != null) { + if (logger.isLoggable(Level.FINE)) + logger.fine("expunge existing " + msgnum); + msg.setExpunged(true); + } + if (seqnums == null) { // time to fill it in + logger.fine("create seqnums array"); + seqnums = new int[messages.length]; + for (int i = 1; i < msgnum; i++) + seqnums[i-1] = i; + seqnums[msgnum - 1] = 0; + for (int i = msgnum + 1; i <= seqnums.length; i++) + seqnums[i-1] = i - 1; + } else { + seqnums[msgnum - 1] = 0; + for (int i = msgnum + 1; i <= seqnums.length; i++) { + assert seqnums[i-1] != 1; + if (seqnums[i-1] > 0) + seqnums[i-1]--; + } + } + } + + /** + * Remove all the expunged messages from the array, + * returning a list of removed message objects. + * + * @return the removed messages + */ + public IMAPMessage[] removeExpungedMessages() { + logger.fine("remove expunged messages"); + // list of expunged messages + List mlist = new ArrayList<>(); + + /* + * Walk through the array compressing it by copying + * higher numbered messages further down in the array, + * effectively removing expunged messages from the array. + * oldnum is the index we use to walk through the array. + * newnum is the index where we copy the next valid message. + * oldnum == newnum until we encounter an expunged message. + */ + int oldnum = 1; + int newnum = 1; + while (oldnum <= size) { + // is message expunged? + if (seqnumOf(oldnum) <= 0) { + IMAPMessage m = getMessage(oldnum); + mlist.add(m); + } else { + // keep this message + if (newnum != oldnum) { + // move message down in the array (compact array) + messages[newnum-1] = messages[oldnum-1]; + if (messages[newnum-1] != null) + messages[newnum-1].setMessageNumber(newnum); + } + newnum++; + } + oldnum++; + } + seqnums = null; + shrink(newnum, oldnum); + + IMAPMessage[] rmsgs = new IMAPMessage[mlist.size()]; + if (logger.isLoggable(Level.FINE)) + logger.fine("return " + rmsgs.length); + mlist.toArray(rmsgs); + return rmsgs; + } + + /** + * Remove expunged messages in msgs from the array, + * returning a list of removed message objects. + * All messages in msgs must be IMAPMessage objects + * from this folder. + * + * @param msgs the messages + * @return the removed messages + */ + public IMAPMessage[] removeExpungedMessages(Message[] msgs) { + logger.fine("remove expunged messages"); + // list of expunged messages + List mlist = new ArrayList<>(); + + /* + * Copy the message numbers of the expunged messages into + * a separate array and sort the array to make it easier to + * process later. + */ + int[] mnum = new int[msgs.length]; + for (int i = 0; i < msgs.length; i++) + mnum[i] = msgs[i].getMessageNumber(); + Arrays.sort(mnum); + + /* + * Walk through the array compressing it by copying + * higher numbered messages further down in the array, + * effectively removing expunged messages from the array. + * oldnum is the index we use to walk through the array. + * newnum is the index where we copy the next valid message. + * oldnum == newnum until we encounter an expunged message. + * + * Even though we know the message number of the first possibly + * expunged message, we still start scanning at message number 1 + * so that we can check whether there's any message whose + * sequence number is different than its message number. If there + * is, we can't throw away the seqnums array when we're done. + */ + int oldnum = 1; + int newnum = 1; + int mnumi = 0; // index into mnum + boolean keepSeqnums = false; + while (oldnum <= size) { + /* + * Are there still expunged messsages in msgs to consider, + * and is the message we're considering the next one in the + * list, and is it expunged? + */ + if (mnumi < mnum.length && + oldnum == mnum[mnumi] && + seqnumOf(oldnum) <= 0) { + IMAPMessage m = getMessage(oldnum); + mlist.add(m); + /* + * Just in case there are duplicate entries in the msgs array, + * we keep advancing mnumi past any duplicates, but of course + * stop when we get to the end of the array. + */ + while (mnumi < mnum.length && mnum[mnumi] <= oldnum) + mnumi++; // consider next message in array + } else { + // keep this message + if (newnum != oldnum) { + // move message down in the array (compact array) + messages[newnum-1] = messages[oldnum-1]; + if (messages[newnum-1] != null) + messages[newnum-1].setMessageNumber(newnum); + if (seqnums != null) + seqnums[newnum-1] = seqnums[oldnum-1]; + } + if (seqnums != null && seqnums[newnum-1] != newnum) + keepSeqnums = true; + newnum++; + } + oldnum++; + } + + if (!keepSeqnums) + seqnums = null; + shrink(newnum, oldnum); + + IMAPMessage[] rmsgs = new IMAPMessage[mlist.size()]; + if (logger.isLoggable(Level.FINE)) + logger.fine("return " + rmsgs.length); + mlist.toArray(rmsgs); + return rmsgs; + } + + /** + * Shrink the messages and seqnums arrays. newend is one past last + * valid element. oldend is one past the previous last valid element. + */ + private void shrink(int newend, int oldend) { + size = newend - 1; + if (logger.isLoggable(Level.FINE)) + logger.fine("size now " + size); + if (size == 0) { // no messages left + messages = null; + seqnums = null; + } else if (size > SLOP && size < messages.length / 2) { + // if array shrinks by too much, reallocate it + logger.fine("reallocate array"); + IMAPMessage[] newm = new IMAPMessage[size + SLOP]; + System.arraycopy(messages, 0, newm, 0, size); + messages = newm; + if (seqnums != null) { + int[] news = new int[size + SLOP]; + System.arraycopy(seqnums, 0, news, 0, size); + seqnums = news; + } + } else { + if (logger.isLoggable(Level.FINE)) + logger.fine("clean " + newend + " to " + oldend); + // clear out unused entries in array + for (int msgnum = newend; msgnum < oldend; msgnum++) { + messages[msgnum-1] = null; + if (seqnums != null) + seqnums[msgnum-1] = 0; + } + } + } + + /** + * Add count messages to the cache. + * newSeqNum is the sequence number of the first message added. + * + * @param count the number of messges + * @param newSeqNum sequence number of first message + */ + public void addMessages(int count, int newSeqNum) { + if (logger.isLoggable(Level.FINE)) + logger.fine("add " + count + " messages"); + // don't have to do anything other than making sure there's space + ensureCapacity(size + count, newSeqNum); + } + + /* + * Make sure the arrays are at least big enough to hold + * "newsize" messages. + */ + private void ensureCapacity(int newsize, int newSeqNum) { + if (messages == null) + messages = new IMAPMessage[newsize + SLOP]; + else if (messages.length < newsize) { + if (logger.isLoggable(Level.FINE)) + logger.fine("expand capacity to " + newsize); + IMAPMessage[] newm = new IMAPMessage[newsize + SLOP]; + System.arraycopy(messages, 0, newm, 0, messages.length); + messages = newm; + if (seqnums != null) { + int[] news = new int[newsize + SLOP]; + System.arraycopy(seqnums, 0, news, 0, seqnums.length); + for (int i = size; i < news.length; i++) + news[i] = newSeqNum++; + seqnums = news; + if (logger.isLoggable(Level.FINE)) + logger.fine("message " + newsize + + " has sequence number " + seqnums[newsize-1]); + } + } else if (newsize < size) { // shrinking? + // this should never happen + if (logger.isLoggable(Level.FINE)) + logger.fine("shrink capacity to " + newsize); + for (int msgnum = newsize + 1; msgnum <= size; msgnum++) { + messages[msgnum-1] = null; + if (seqnums != null) + seqnums[msgnum-1] = -1; + } + } + size = newsize; + } + + /** + * Return the sequence number for the given message number. + * + * @param msgnum the message number + * @return the sequence number + */ + public int seqnumOf(int msgnum) { + if (seqnums == null) + return msgnum; + else { + if (logger.isLoggable(Level.FINE)) + logger.fine("msgnum " + msgnum + " is seqnum " + + seqnums[msgnum-1]); + return seqnums[msgnum-1]; + } + } + + /** + * Return the message number for the given sequence number. + */ + private int msgnumOf(int seqnum) { + if (seqnums == null) + return seqnum; + if (seqnum < 1) { // should never happen + if (logger.isLoggable(Level.FINE)) + logger.fine("bad seqnum " + seqnum); + return -1; + } + for (int msgnum = seqnum; msgnum <= size; msgnum++) { + if (seqnums[msgnum-1] == seqnum) + return msgnum; + if (seqnums[msgnum-1] > seqnum) + break; // message doesn't exist + } + return -1; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/MessageVanishedEvent.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/MessageVanishedEvent.java new file mode 100644 index 000000000..9279a3fab --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/MessageVanishedEvent.java @@ -0,0 +1,86 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import javax.mail.Folder; +import javax.mail.Message; +import javax.mail.event.MessageCountEvent; + +/** + * This class provides notification of messages that have been removed + * since the folder was last synchronized. + * + * @since JavaMail 1.5.1 + * @author Bill Shannon + */ + +public class MessageVanishedEvent extends MessageCountEvent { + + /** + * The message UIDs. + */ + private long[] uids; + + // a reusable empty array + private static final Message[] noMessages = { }; + + private static final long serialVersionUID = 2142028010250024922L; + + /** + * Constructor. + * + * @param folder the containing folder + * @param uids the UIDs for the vanished messages + */ + public MessageVanishedEvent(Folder folder, long[] uids) { + super(folder, REMOVED, true, noMessages); + this.uids = uids; + } + + /** + * Return the UIDs for this event. + * + * @return the UIDs + */ + public long[] getUIDs() { + return uids; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/ModifiedSinceTerm.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/ModifiedSinceTerm.java new file mode 100644 index 000000000..57ad42158 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/ModifiedSinceTerm.java @@ -0,0 +1,118 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import javax.mail.Message; +import javax.mail.search.SearchTerm; + +/** + * Find messages that have been modified since a given MODSEQ value. + * Relies on the server implementing the CONDSTORE extension + * (RFC 4551). + * + * @since JavaMail 1.5.1 + * @author Bill Shannon + */ +public final class ModifiedSinceTerm extends SearchTerm { + + private long modseq; + + private static final long serialVersionUID = 5151457469634727992L; + + /** + * Constructor. + * + * @param modseq modification sequence number + */ + public ModifiedSinceTerm(long modseq) { + this.modseq = modseq; + } + + /** + * Return the modseq. + * + * @return the modseq + */ + public long getModSeq() { + return modseq; + } + + /** + * The match method. + * + * @param msg the date comparator is applied to this Message's + * MODSEQ + * @return true if the comparison succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + long m; + + try { + if (msg instanceof IMAPMessage) + m = ((IMAPMessage)msg).getModSeq(); + else + return false; + } catch (Exception e) { + return false; + } + + return m >= modseq; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ModifiedSinceTerm)) + return false; + return modseq == ((ModifiedSinceTerm)obj).modseq; + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return (int)modseq; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/OlderTerm.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/OlderTerm.java new file mode 100644 index 000000000..b81dbc01f --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/OlderTerm.java @@ -0,0 +1,120 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.util.Date; +import javax.mail.Message; +import javax.mail.search.SearchTerm; + +/** + * Find messages that are older than a given interval (in seconds). + * Relies on the server implementing the WITHIN search extension + * (RFC 5032). + * + * @since JavaMail 1.5.1 + * @author Bill Shannon + */ +public final class OlderTerm extends SearchTerm { + + private int interval; + + private static final long serialVersionUID = 3951078948727995682L; + + /** + * Constructor. + * + * @param interval number of seconds older + */ + public OlderTerm(int interval) { + this.interval = interval; + } + + /** + * Return the interval. + * + * @return the interval + */ + public int getInterval() { + return interval; + } + + /** + * The match method. + * + * @param msg the date comparator is applied to this Message's + * received date + * @return true if the comparison succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + Date d; + + try { + d = msg.getReceivedDate(); + } catch (Exception e) { + return false; + } + + if (d == null) + return false; + + return d.getTime() <= + System.currentTimeMillis() - ((long)interval * 1000); + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof OlderTerm)) + return false; + return interval == ((OlderTerm)obj).interval; + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return interval; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/ReferralException.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/ReferralException.java new file mode 100644 index 000000000..d99b9ab0f --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/ReferralException.java @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import javax.mail.AuthenticationFailedException; + +/** + * A special kind of AuthenticationFailedException that indicates that + * the reason for the failure was an IMAP REFERRAL in the response code. + * See RFC 2221 for details. + * + * @since JavaMail 1.5.5 + */ + +public class ReferralException extends AuthenticationFailedException { + + private String url; + private String text; + + private static final long serialVersionUID = -3414063558596287683L; + + /** + * Constructs an ReferralException with the specified URL and text. + * + * @param text the detail message + * @param url the URL + */ + public ReferralException(String url, String text) { + super("[REFERRAL " + url + "] " + text); + this.url = url; + this.text = text; + } + + /** + * Return the IMAP URL in the referral. + * + * @return the IMAP URL + */ + public String getUrl() { + return url; + } + + /** + * Return the text sent by the server along with the referral. + * + * @return the text + */ + public String getText() { + return text; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/ResyncData.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/ResyncData.java new file mode 100644 index 000000000..2719ffc50 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/ResyncData.java @@ -0,0 +1,142 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import com.sun.mail.imap.protocol.UIDSet; + +/** + * Resynchronization data as defined by the QRESYNC extension + * (RFC 5162). + * An instance of ResyncData is supplied to the + * {@link com.sun.mail.imap.IMAPFolder#open(int,com.sun.mail.imap.ResyncData) + * IMAPFolder open} method. + * The CONDSTORE ResyncData instance is used to enable the + * CONDSTORE extension + * (RFC 4551). + * A ResyncData instance with uidvalidity and modseq values + * is used to enable the QRESYNC extension. + * + * @since JavaMail 1.5.1 + * @author Bill Shannon + */ + +public class ResyncData { + private long uidvalidity = -1; + private long modseq = -1; + private UIDSet[] uids = null; + + /** + * Used to enable only the CONDSTORE extension. + */ + public static final ResyncData CONDSTORE = new ResyncData(-1, -1); + + /** + * Used to report on changes since the specified modseq. + * If the UIDVALIDITY of the folder has changed, no message + * changes will be reported. The application must check the + * UIDVALIDITY of the folder after open to make sure it's + * the expected folder. + * + * @param uidvalidity the UIDVALIDITY + * @param modseq the MODSEQ + */ + public ResyncData(long uidvalidity, long modseq) { + this.uidvalidity = uidvalidity; + this.modseq = modseq; + this.uids = null; + } + + /** + * Used to limit the reported message changes to those with UIDs + * in the specified range. + * + * @param uidvalidity the UIDVALIDITY + * @param modseq the MODSEQ + * @param uidFirst the first UID + * @param uidLast the last UID + */ + public ResyncData(long uidvalidity, long modseq, + long uidFirst, long uidLast) { + this.uidvalidity = uidvalidity; + this.modseq = modseq; + this.uids = new UIDSet[] { new UIDSet(uidFirst, uidLast) }; + } + + /** + * Used to limit the reported message changes to those with the + * specified UIDs. + * + * @param uidvalidity the UIDVALIDITY + * @param modseq the MODSEQ + * @param uids the UID values + */ + public ResyncData(long uidvalidity, long modseq, long[] uids) { + this.uidvalidity = uidvalidity; + this.modseq = modseq; + this.uids = UIDSet.createUIDSets(uids); + } + + /** + * Get the UIDVALIDITY value specified when this instance was created. + * + * @return the UIDVALIDITY value + */ + public long getUIDValidity() { + return uidvalidity; + } + + /** + * Get the MODSEQ value specified when this instance was created. + * + * @return the MODSEQ value + */ + public long getModSeq() { + return modseq; + } + + /* + * Package private. IMAPProtocol gets this data indirectly + * using Utility.getResyncUIDSet(). + */ + UIDSet[] getUIDSet() { + return uids; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/Rights.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/Rights.java new file mode 100644 index 000000000..11f278905 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/Rights.java @@ -0,0 +1,468 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.util.*; + +/** + * The Rights class represents the set of rights for an authentication + * identifier (for instance, a user or a group).

    + * + * A right is represented by the Rights.Right + * inner class.

    + * + * A set of standard rights are predefined (see RFC 2086). Most folder + * implementations are expected to support these rights. Some + * implementations may also support site-defined rights.

    + * + * The following code sample illustrates how to examine your + * rights for a folder. + *

    + *
    + * Rights rights = folder.myRights();
    + *
    + * // Check if I can write this folder
    + * if (rights.contains(Rights.Right.WRITE))
    + *	System.out.println("Can write folder");
    + *
    + * // Now give Joe all my rights, except the ability to write the folder
    + * rights.remove(Rights.Right.WRITE);
    + * ACL acl = new ACL("joe", rights);
    + * folder.setACL(acl);
    + * 
    + *

    + * + * @author Bill Shannon + */ + +public class Rights implements Cloneable { + + private boolean[] rights = new boolean[128]; // XXX + + /** + * This inner class represents an individual right. A set + * of standard rights objects are predefined here. + */ + public static final class Right { + private static Right[] cache = new Right[128]; + + // XXX - initialization order? + /** + * Lookup - mailbox is visible to LIST/LSUB commands. + */ + public static final Right LOOKUP = getInstance('l'); + + /** + * Read - SELECT the mailbox, perform CHECK, FETCH, PARTIAL, + * SEARCH, COPY from mailbox + */ + public static final Right READ = getInstance('r'); + + /** + * Keep seen/unseen information across sessions - STORE \SEEN flag. + */ + public static final Right KEEP_SEEN = getInstance('s'); + + /** + * Write - STORE flags other than \SEEN and \DELETED. + */ + public static final Right WRITE = getInstance('w'); + + /** + * Insert - perform APPEND, COPY into mailbox. + */ + public static final Right INSERT = getInstance('i'); + + /** + * Post - send mail to submission address for mailbox, + * not enforced by IMAP4 itself. + */ + public static final Right POST = getInstance('p'); + + /** + * Create - CREATE new sub-mailboxes in any implementation-defined + * hierarchy, RENAME or DELETE mailbox. + */ + public static final Right CREATE = getInstance('c'); + + /** + * Delete - STORE \DELETED flag, perform EXPUNGE. + */ + public static final Right DELETE = getInstance('d'); + + /** + * Administer - perform SETACL. + */ + public static final Right ADMINISTER = getInstance('a'); + + char right; // the right represented by this Right object + + /** + * Private constructor used only by getInstance. + */ + private Right(char right) { + if ((int)right >= 128) + throw new IllegalArgumentException("Right must be ASCII"); + this.right = right; + } + + /** + * Get a Right object representing the specified character. + * Characters are assigned per RFC 2086. + * + * @param right the character representing the right + * @return the Right object + */ + public static synchronized Right getInstance(char right) { + if ((int)right >= 128) + throw new IllegalArgumentException("Right must be ASCII"); + if (cache[(int)right] == null) + cache[(int)right] = new Right(right); + return cache[(int)right]; + } + + @Override + public String toString() { + return String.valueOf(right); + } + } + + + /** + * Construct an empty Rights object. + */ + public Rights() { } + + /** + * Construct a Rights object initialized with the given rights. + * + * @param rights the rights for initialization + */ + public Rights(Rights rights) { + System.arraycopy(rights.rights, 0, this.rights, 0, this.rights.length); + } + + /** + * Construct a Rights object initialized with the given rights. + * + * @param rights the rights for initialization + */ + public Rights(String rights) { + for (int i = 0; i < rights.length(); i++) + add(Right.getInstance(rights.charAt(i))); + } + + /** + * Construct a Rights object initialized with the given right. + * + * @param right the right for initialization + */ + public Rights(Right right) { + this.rights[(int)right.right] = true; + } + + /** + * Add the specified right to this Rights object. + * + * @param right the right to add + */ + public void add(Right right) { + this.rights[(int)right.right] = true; + } + + /** + * Add all the rights in the given Rights object to this + * Rights object. + * + * @param rights Rights object + */ + public void add(Rights rights) { + for (int i = 0; i < rights.rights.length; i++) + if (rights.rights[i]) + this.rights[i] = true; + } + + /** + * Remove the specified right from this Rights object. + * + * @param right the right to be removed + */ + public void remove(Right right) { + this.rights[(int)right.right] = false; + } + + /** + * Remove all rights in the given Rights object from this + * Rights object. + * + * @param rights the rights to be removed + */ + public void remove(Rights rights) { + for (int i = 0; i < rights.rights.length; i++) + if (rights.rights[i]) + this.rights[i] = false; + } + + /** + * Check whether the specified right is present in this Rights object. + * + * @param right the Right to check + * @return true of the given right is present, otherwise false. + */ + public boolean contains(Right right) { + return this.rights[(int)right.right]; + } + + /** + * Check whether all the rights in the specified Rights object are + * present in this Rights object. + * + * @param rights the Rights to check + * @return true if all rights in the given Rights object are present, + * otherwise false. + */ + public boolean contains(Rights rights) { + for (int i = 0; i < rights.rights.length; i++) + if (rights.rights[i] && !this.rights[i]) + return false; + + // If we've made it till here, return true + return true; + } + + /** + * Check whether the two Rights objects are equal. + * + * @return true if they're equal + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Rights)) + return false; + + Rights rights = (Rights)obj; + + for (int i = 0; i < rights.rights.length; i++) + if (rights.rights[i] != this.rights[i]) + return false; + + return true; + } + + /** + * Compute a hash code for this Rights object. + * + * @return the hash code + */ + @Override + public int hashCode() { + int hash = 0; + for (int i = 0; i < this.rights.length; i++) + if (this.rights[i]) + hash++; + return hash; + } + + /** + * Return all the rights in this Rights object. Returns + * an array of size zero if no rights are set. + * + * @return array of Rights.Right objects representing rights + */ + public Right[] getRights() { + List v = new ArrayList<>(); + for (int i = 0; i < this.rights.length; i++) + if (this.rights[i]) + v.add(Right.getInstance((char)i)); + return v.toArray(new Right[v.size()]); + } + + /** + * Returns a clone of this Rights object. + */ + @Override + public Object clone() { + Rights r = null; + try { + r = (Rights)super.clone(); + r.rights = new boolean[128]; + System.arraycopy(this.rights, 0, r.rights, 0, this.rights.length); + } catch (CloneNotSupportedException cex) { + // ignore, can't happen + } + return r; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < this.rights.length; i++) + if (this.rights[i]) + sb.append((char)i); + return sb.toString(); + } + + /***** + public static void main(String argv[]) throws Exception { + // a new rights object + Rights f1 = new Rights(); + f1.add(Rights.Right.READ); + f1.add(Rights.Right.WRITE); + f1.add(Rights.Right.CREATE); + f1.add(Rights.Right.DELETE); + + // check copy constructor + Rights fc = new Rights(f1); + if (f1.equals(fc) && fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // check clone + fc = (Rights)f1.clone(); + if (f1.equals(fc) && fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // add a right and make sure it still works right + f1.add(Rights.Right.ADMINISTER); + + // shouldn't be equal here + if (!f1.equals(fc) && !fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // check clone + fc = (Rights)f1.clone(); + if (f1.equals(fc) && fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + fc.add(Rights.Right.INSERT); + if (!f1.equals(fc) && !fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // check copy constructor + fc = new Rights(f1); + if (f1.equals(fc) && fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // another new rights object + Rights f2 = new Rights(Rights.Right.READ); + f2.add(Rights.Right.WRITE); + + if (f1.contains(Rights.Right.READ)) + System.out.println("success"); + else + System.out.println("fail"); + + if (f1.contains(Rights.Right.WRITE)) + System.out.println("success"); + else + System.out.println("fail"); + + if (f1.contains(Rights.Right.CREATE)) + System.out.println("success"); + else + System.out.println("fail"); + + if (f1.contains(Rights.Right.DELETE)) + System.out.println("success"); + else + System.out.println("fail"); + + if (f2.contains(Rights.Right.WRITE)) + System.out.println("success"); + else + System.out.println("fail"); + + + System.out.println("----------------"); + + Right[] r = f1.getRights(); + for (int i = 0; i < r.length; i++) + System.out.println(r[i]); + System.out.println("----------------"); + + if (f1.contains(f2)) // this should be true + System.out.println("success"); + else + System.out.println("fail"); + + if (!f2.contains(f1)) // this should be false + System.out.println("success"); + else + System.out.println("fail"); + + Rights f3 = new Rights(); + f3.add(Rights.Right.READ); + f3.add(Rights.Right.WRITE); + f3.add(Rights.Right.CREATE); + f3.add(Rights.Right.DELETE); + f3.add(Rights.Right.ADMINISTER); + f3.add(Rights.Right.LOOKUP); + + f1.add(Rights.Right.LOOKUP); + + if (f1.equals(f3)) + System.out.println("equals success"); + else + System.out.println("fail"); + if (f3.equals(f1)) + System.out.println("equals success"); + else + System.out.println("fail"); + System.out.println("f1 hash code " + f1.hashCode()); + System.out.println("f3 hash code " + f3.hashCode()); + if (f1.hashCode() == f3.hashCode()) + System.out.println("success"); + else + System.out.println("fail"); + } + ****/ +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/SortTerm.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/SortTerm.java new file mode 100644 index 000000000..6ff30cb28 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/SortTerm.java @@ -0,0 +1,105 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +/** + * A particular sort criteria, as defined by + * RFC 5256. + * Sort criteria are used with the + * {@link IMAPFolder#getSortedMessages getSortedMessages} method. + * Multiple sort criteria are specified in an array with the order in + * the array specifying the order in which the sort criteria are applied. + * + * @since JavaMail 1.4.4 + */ +public final class SortTerm { + /** + * Sort by message arrival date and time. + */ + public static final SortTerm ARRIVAL = new SortTerm("ARRIVAL"); + + /** + * Sort by email address of first Cc recipient. + */ + public static final SortTerm CC = new SortTerm("CC"); + + /** + * Sort by sent date and time. + */ + public static final SortTerm DATE = new SortTerm("DATE"); + + /** + * Sort by first From email address. + */ + public static final SortTerm FROM = new SortTerm("FROM"); + + /** + * Reverse the sort order of the following item. + */ + public static final SortTerm REVERSE = new SortTerm("REVERSE"); + + /** + * Sort by the message size. + */ + public static final SortTerm SIZE = new SortTerm("SIZE"); + + /** + * Sort by the base subject text. Note that the "base subject" + * is defined by RFC 5256 and doesn't include items such as "Re:" + * in the subject header. + */ + public static final SortTerm SUBJECT = new SortTerm("SUBJECT"); + + /** + * Sort by email address of first To recipient. + */ + public static final SortTerm TO = new SortTerm("TO"); + + private String term; + private SortTerm(String term) { + this.term = term; + } + + @Override + public String toString() { + return term; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/Utility.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/Utility.java new file mode 100644 index 000000000..0490a299c --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/Utility.java @@ -0,0 +1,238 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.util.Arrays; +import java.util.Comparator; + +import javax.mail.*; + +import com.sun.mail.imap.protocol.MessageSet; +import com.sun.mail.imap.protocol.UIDSet; +import java.util.ArrayList; +import java.util.List; + +/** + * Holder for some static utility methods. + * + * @author John Mani + * @author Bill Shannon + */ + +public final class Utility { + + // Cannot be initialized + private Utility() { } + + /** + * Run thru the given array of messages, apply the given + * Condition on each message and generate sets of contiguous + * sequence-numbers for the successful messages. If a message + * in the given array is found to be expunged, it is ignored. + * + * ASSERT: Since this method uses and returns message sequence + * numbers, you should use this method only when holding the + * messageCacheLock. + * + * @param msgs the messages + * @param cond the condition to check + * @return the MessageSet array + */ + public static MessageSet[] toMessageSet(Message[] msgs, Condition cond) { + List v = new ArrayList<>(1); + int current, next; + + IMAPMessage msg; + for (int i = 0; i < msgs.length; i++) { + msg = (IMAPMessage)msgs[i]; + if (msg.isExpunged()) // expunged message, skip it + continue; + + current = msg.getSequenceNumber(); + // Apply the condition. If it fails, skip it. + if ((cond != null) && !cond.test(msg)) + continue; + + MessageSet set = new MessageSet(); + set.start = current; + + // Look for contiguous sequence numbers + for (++i; i < msgs.length; i++) { + // get next message + msg = (IMAPMessage)msgs[i]; + + if (msg.isExpunged()) // expunged message, skip it + continue; + next = msg.getSequenceNumber(); + + // Does this message match our condition ? + if ((cond != null) && !cond.test(msg)) + continue; + + if (next == current+1) + current = next; + else { // break in sequence + // We need to reexamine this message at the top of + // the outer loop, so decrement 'i' to cancel the + // outer loop's autoincrement + i--; + break; + } + } + set.end = current; + v.add(set); + } + + if (v.isEmpty()) // No valid messages + return null; + else { + return v.toArray(new MessageSet[v.size()]); + } + } + /** + * Sort (a copy of) the given array of messages and then + * run thru the sorted array of messages, apply the given + * Condition on each message and generate sets of contiguous + * sequence-numbers for the successful messages. If a message + * in the given array is found to be expunged, it is ignored. + * + * ASSERT: Since this method uses and returns message sequence + * numbers, you should use this method only when holding the + * messageCacheLock. + * + * @param msgs the messages + * @param cond the condition to check + * @return the MessageSet array + * @since JavaMail 1.5.4 + */ + public static MessageSet[] toMessageSetSorted(Message[] msgs, + Condition cond) { + /* + * XXX - This is quick and dirty. A more efficient strategy would be + * to generate an array of message numbers by applying the condition + * (with zero indicating the message doesn't satisfy the condition), + * sort it, and then convert it to a MessageSet skipping all the zeroes. + */ + msgs = msgs.clone(); + Arrays.sort(msgs, + new Comparator() { + @Override + public int compare(Message msg1, Message msg2) { + return msg1.getMessageNumber() - msg2.getMessageNumber(); + } + }); + return toMessageSet(msgs, cond); + } + + /** + * Return UIDSets for the messages. Note that the UIDs + * must have already been fetched for the messages. + * + * @param msgs the messages + * @return the UIDSet array + */ + public static UIDSet[] toUIDSet(Message[] msgs) { + List v = new ArrayList<>(1); + long current, next; + + IMAPMessage msg; + for (int i = 0; i < msgs.length; i++) { + msg = (IMAPMessage)msgs[i]; + if (msg.isExpunged()) // expunged message, skip it + continue; + + current = msg.getUID(); + + UIDSet set = new UIDSet(); + set.start = current; + + // Look for contiguous UIDs + for (++i; i < msgs.length; i++) { + // get next message + msg = (IMAPMessage)msgs[i]; + + if (msg.isExpunged()) // expunged message, skip it + continue; + next = msg.getUID(); + + if (next == current+1) + current = next; + else { // break in sequence + // We need to reexamine this message at the top of + // the outer loop, so decrement 'i' to cancel the + // outer loop's autoincrement + i--; + break; + } + } + set.end = current; + v.add(set); + } + + if (v.isEmpty()) // No valid messages + return null; + else { + return v.toArray(new UIDSet[v.size()]); + } + } + + /** + * Make the ResyncData UIDSet available to IMAPProtocol, + * which is in a different package. Note that this class + * is not included in the public javadocs, thus "hiding" + * this method. + * + * @param rd the ResyncData + * @return the UIDSet array + * @since JavaMail 1.5.1 + */ + public static UIDSet[] getResyncUIDSet(ResyncData rd) { + return rd.getUIDSet(); + } + + /** + * This interface defines the test to be executed in + * toMessageSet(). + */ + public static interface Condition { + public boolean test(IMAPMessage message); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/YoungerTerm.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/YoungerTerm.java new file mode 100644 index 000000000..535a57e31 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/YoungerTerm.java @@ -0,0 +1,120 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap; + +import java.util.Date; +import javax.mail.Message; +import javax.mail.search.SearchTerm; + +/** + * Find messages that are younger than a given interval (in seconds). + * Relies on the server implementing the WITHIN search extension + * (RFC 5032). + * + * @since JavaMail 1.5.1 + * @author Bill Shannon + */ +public final class YoungerTerm extends SearchTerm { + + private int interval; + + private static final long serialVersionUID = 1592714210688163496L; + + /** + * Constructor. + * + * @param interval number of seconds younger + */ + public YoungerTerm(int interval) { + this.interval = interval; + } + + /** + * Return the interval. + * + * @return the interval + */ + public int getInterval() { + return interval; + } + + /** + * The match method. + * + * @param msg the date comparator is applied to this Message's + * received date + * @return true if the comparison succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + Date d; + + try { + d = msg.getReceivedDate(); + } catch (Exception e) { + return false; + } + + if (d == null) + return false; + + return d.getTime() >= + System.currentTimeMillis() - ((long)interval * 1000); + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof YoungerTerm)) + return false; + return interval == ((YoungerTerm)obj).interval; + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return interval; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/package.html b/fine-third-default/fine-mail/src/com/sun/mail/imap/package.html new file mode 100644 index 000000000..8d0e150e1 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/package.html @@ -0,0 +1,1024 @@ + + + + + + +com.sun.mail.imap package + + + +

    +An IMAP protocol provider for the JavaMail API +that provides access to an IMAP message store. +Both the IMAP4 and IMAP4rev1 protocols are supported. +Refer to +RFC 3501 +for more information. +The IMAP protocol provider also supports many IMAP extensions (described below). +Note that the server needs to support these extensions (and not all servers do) +in order to use the support in the IMAP provider. +You can query the server for support of these extensions using the +{@link com.sun.mail.imap.IMAPStore#hasCapability IMAPStore hasCapability} +method using the capability name defined by the extension +(see the appropriate RFC) after connecting to the server. +

    +UIDPLUS Support +

    +The IMAP UIDPLUS extension +(RFC 4315) +is supported via the IMAPFolder methods +{@link com.sun.mail.imap.IMAPFolder#addMessages addMessages}, +{@link com.sun.mail.imap.IMAPFolder#appendUIDMessages appendUIDMessages}, and +{@link com.sun.mail.imap.IMAPFolder#copyUIDMessages copyUIDMessages}. +

    +MOVE Support +

    +The IMAP MOVE extension +(RFC 6851) +is supported via the IMAPFolder methods +{@link com.sun.mail.imap.IMAPFolder#moveMessages moveMessages} and +{@link com.sun.mail.imap.IMAPFolder#moveUIDMessages moveUIDMessages}. +

    +SASL Support +

    +The IMAP protocol provider can use SASL +(RFC 4422) +authentication mechanisms on systems that support the +javax.security.sasl APIs. +The SASL-IR +(RFC 4959) +capability is also supported. +In addition to the SASL mechanisms that are built into +the SASL implementation, users can also provide additional +SASL mechanisms of their own design to support custom authentication +schemes. See the + +Java SASL API Programming and Deployment Guide for details. +Note that the current implementation doesn't support SASL mechanisms +that provide their own integrity or confidentiality layer. +

    +OAuth 2.0 Support +

    +Support for OAuth 2.0 authentication via the + +XOAUTH2 authentication mechanism is provided either through the SASL +support described above or as a built-in authentication mechanism in the +IMAP provider. +The OAuth 2.0 Access Token should be passed as the password for this mechanism. +See +OAuth2 Support for details. +

    +Connection Pool +

    +A connected IMAPStore maintains a pool of IMAP protocol objects for +use in communicating with the IMAP server. The IMAPStore will create +the initial AUTHENTICATED connection and seed the pool with this +connection. As folders are opened and new IMAP protocol objects are +needed, the IMAPStore will provide them from the connection pool, +or create them if none are available. When a folder is closed, +its IMAP protocol object is returned to the connection pool if the +pool is not over capacity. +

    +

    +A mechanism is provided for timing out idle connection pool IMAP +protocol objects. Timed out connections are closed and removed (pruned) +from the connection pool. +

    +

    +The connected IMAPStore object may or may not maintain a separate IMAP +protocol object that provides the store a dedicated connection to the +IMAP server. This is provided mainly for compatibility with previous +implementations of the IMAP protocol provider. +

    +QUOTA Support +

    +The IMAP QUOTA extension +(RFC 2087) +is supported via the +{@link javax.mail.QuotaAwareStore QuotaAwareStore} interface implemented by +{@link com.sun.mail.imap.IMAPStore IMAPStore}, and the +{@link com.sun.mail.imap.IMAPFolder#getQuota IMAPFolder getQuota} and +{@link com.sun.mail.imap.IMAPFolder#setQuota IMAPFolder setQuota} methods. +ACL Support +

    +The IMAP ACL extension +(RFC 2086) +is supported via the +{@link com.sun.mail.imap.Rights Rights} class and the IMAPFolder methods +{@link com.sun.mail.imap.IMAPFolder#getACL getACL}, +{@link com.sun.mail.imap.IMAPFolder#addACL addACL}, +{@link com.sun.mail.imap.IMAPFolder#removeACL removeACL}, +{@link com.sun.mail.imap.IMAPFolder#addRights addRights}, +{@link com.sun.mail.imap.IMAPFolder#removeRights removeRights}, +{@link com.sun.mail.imap.IMAPFolder#listRights listRights}, and +{@link com.sun.mail.imap.IMAPFolder#myRights myRights}. +

    +SORT Support +

    +The IMAP SORT extension +(RFC 5256) +is supported via the +{@link com.sun.mail.imap.SortTerm SortTerm} class and the IMAPFolder +{@link com.sun.mail.imap.IMAPFolder#getSortedMessages getSortedMessages} +methods. +

    +CONDSTORE and QRESYNC Support +

    +Basic support is provided for the IMAP CONDSTORE +(RFC 4551) +and QRESYNC +(RFC 5162) +extensions for the purpose of resynchronizing a folder after offline operation. +Of course, the server must support these extensions. +Use of these extensions is enabled by using the new +{@link com.sun.mail.imap.IMAPFolder#open(int,com.sun.mail.imap.ResyncData) +IMAPFolder open} method and supplying an appropriate +{@link com.sun.mail.imap.ResyncData ResyncData} instance. +Using +{@link com.sun.mail.imap.ResyncData#CONDSTORE ResyncData.CONDSTORE} +enables the CONDSTORE extension, which allows you to discover the +modification sequence number (modseq) of messages using the +{@link com.sun.mail.imap.IMAPMessage#getModSeq IMAPMessage getModSeq} +method and the +{@link com.sun.mail.imap.IMAPFolder#getHighestModSeq +IMAPFolder getHighestModSeq} method. +Using a +{@link com.sun.mail.imap.ResyncData ResyncData} instance with appropriate +values also allows the server to report any changes in messages since the last +resynchronization. +The changes are reported as a list of +{@link javax.mail.event.MailEvent MailEvent} instances. +The special +{@link com.sun.mail.imap.MessageVanishedEvent MessageVanishedEvent} reports on +UIDs of messages that have been removed since the last resynchronization. +A +{@link javax.mail.event.MessageChangedEvent MessageChangedEvent} reports on +changes to flags of messages. +For example: +

    +
    +	Folder folder = store.getFolder("whatever");
    +	IMAPFolder ifolder = (IMAPFolder)folder;
    +	List<MailEvent> events = ifolder.open(Folder.READ_WRITE,
    +		    new ResyncData(prevUidValidity, prevModSeq));
    +	for (MailEvent ev : events) {
    +	    if (ev instanceOf MessageChangedEvent) {
    +		// process flag changes
    +	    } else if (ev instanceof MessageVanishedEvent) {
    +		// process messages that were removed
    +	    }
    +	}
    +
    +

    +See the referenced RFCs for more details on these IMAP extensions. +

    +WITHIN Search Support +

    +The IMAP WITHIN search extension +(RFC 5032) +is supported via the +{@link com.sun.mail.imap.YoungerTerm YoungerTerm} and +{@link com.sun.mail.imap.OlderTerm OlderTerm} +{@link javax.mail.search.SearchTerm SearchTerms}, which can be used as follows: +

    +
    +	// search for messages delivered in the last day
    +	Message[] msgs = folder.search(new YoungerTerm(24 * 60 * 60));
    +
    +LOGIN-REFERRAL Support +

    +The IMAP LOGIN-REFERRAL extension +(RFC 2221) +is supported. +If a login referral is received when connecting or when authentication fails, a +{@link com.sun.mail.imap.ReferralException ReferralException} is thrown. +A referral can also occur when login succeeds. By default, no exception is +thrown in this case. To force an exception to be thrown and the authentication +to fail, set the mail.imap.referralexception property to "true". +

    +COMPRESS Support +

    +The IMAP COMPRESS extension +(RFC 4978) +is supported. +If the server supports the extension and the +mail.imap.compress.enable property is set to "true", +compression will be enabled. +

    +UTF-8 Support +

    +The IMAP UTF8 extension +(RFC 6855) +is supported. +If the server supports the extension, the client will enable use of UTF-8, +allowing use of UTF-8 in IMAP protocol strings such as folder names. +

    +Properties +

    +The IMAP protocol provider supports the following properties, +which may be set in the JavaMail Session object. +The properties are always set as strings; the Type column describes +how the string is interpreted. For example, use +

    +
    +	props.put("mail.imap.port", "888");
    +
    +

    +to set the mail.imap.port property, which is of type int. +

    +

    +Note that if you're using the "imaps" protocol to access IMAP over SSL, +all the properties would be named "mail.imaps.*". +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mail.imap.userStringDefault user name for IMAP.
    mail.imap.hostStringThe IMAP server to connect to.
    mail.imap.portintThe IMAP server port to connect to, if the connect() method doesn't +explicitly specify one. Defaults to 143.
    mail.imap.partialfetchbooleanControls whether the IMAP partial-fetch capability should be used. +Defaults to true.
    mail.imap.fetchsizeintPartial fetch size in bytes. Defaults to 16K.
    mail.imap.peekboolean +If set to true, use the IMAP PEEK option when fetching body parts, +to avoid setting the SEEN flag on messages. +Defaults to false. +Can be overridden on a per-message basis by the +{@link com.sun.mail.imap.IMAPMessage#setPeek setPeek} +method on IMAPMessage. +
    mail.imap.ignorebodystructuresizebooleanThe IMAP BODYSTRUCTURE response includes the exact size of each body part. +Normally, this size is used to determine how much data to fetch for each +body part. +Some servers report this size incorrectly in some cases; this property can +be set to work around such server bugs. +If this property is set to true, this size is ignored and data is fetched +until the server reports the end of data. +This will result in an extra fetch if the data size is a multiple of the +block size. +Defaults to false.
    mail.imap.connectiontimeoutintSocket connection timeout value in milliseconds. +This timeout is implemented by java.net.Socket. +Default is infinite timeout.
    mail.imap.timeoutintSocket read timeout value in milliseconds. +This timeout is implemented by java.net.Socket. +Default is infinite timeout.
    mail.imap.writetimeoutintSocket write timeout value in milliseconds. +This timeout is implemented by using a +java.util.concurrent.ScheduledExecutorService per connection +that schedules a thread to close the socket if the timeout expires. +Thus, the overhead of using this timeout is one thread per connection. +Default is infinite timeout.
    mail.imap.statuscachetimeoutintTimeout value in milliseconds for cache of STATUS command response. +Default is 1000 (1 second). Zero disables cache.
    mail.imap.appendbuffersizeint +Maximum size of a message to buffer in memory when appending to an IMAP +folder. If not set, or set to -1, there is no maximum and all messages +are buffered. If set to 0, no messages are buffered. If set to (e.g.) +8192, messages of 8K bytes or less are buffered, larger messages are +not buffered. Buffering saves cpu time at the expense of short term +memory usage. If you commonly append very large messages to IMAP +mailboxes you might want to set this to a moderate value (1M or less). +
    mail.imap.connectionpoolsizeintMaximum number of available connections in the connection pool. +Default is 1.
    mail.imap.connectionpooltimeoutintTimeout value in milliseconds for connection pool connections. Default +is 45000 (45 seconds).
    mail.imap.separatestoreconnectionbooleanFlag to indicate whether to use a dedicated store connection for store +commands. Default is false.
    mail.imap.allowreadonlyselectbooleanIf false, attempts to open a folder read/write will fail +if the SELECT command succeeds but indicates that the folder is READ-ONLY. +This sometimes indicates that the folder contents can'tbe changed, but +the flags are per-user and can be changed, such as might be the case for +public shared folders. If true, such open attempts will succeed, allowing +the flags to be changed. The getMode method on the +Folder object will return Folder.READ_ONLY +in this case even though the open method specified +Folder.READ_WRITE. Default is false.
    mail.imap.auth.mechanismsString +If set, lists the authentication mechanisms to consider, and the order +in which to consider them. Only mechanisms supported by the server and +supported by the current implementation will be used. +The default is "PLAIN LOGIN NTLM", which includes all +the authentication mechanisms supported by the current implementation +except XOAUTH2. +
    mail.imap.auth.login.disablebooleanIf true, prevents use of the non-standard AUTHENTICATE LOGIN +command, instead using the plain LOGIN command. +Default is false.
    mail.imap.auth.plain.disablebooleanIf true, prevents use of the AUTHENTICATE PLAIN command. +Default is false.
    mail.imap.auth.ntlm.disablebooleanIf true, prevents use of the AUTHENTICATE NTLM command. +Default is false.
    mail.imap.auth.ntlm.domainString +The NTLM authentication domain. +
    mail.imap.auth.ntlm.flagsint +NTLM protocol-specific flags. +See +http://curl.haxx.se/rfc/ntlm.html#theNtlmFlags for details. +
    mail.imap.auth.xoauth2.disablebooleanIf true, prevents use of the AUTHENTICATE XOAUTH2 command. +Because the OAuth 2.0 protocol requires a special access token instead of +a password, this mechanism is disabled by default. Enable it by explicitly +setting this property to "false" or by setting the "mail.imap.auth.mechanisms" +property to "XOAUTH2".
    mail.imap.proxyauth.userStringIf the server supports the PROXYAUTH extension, this property +specifies the name of the user to act as. Authenticate to the +server using the administrator's credentials. After authentication, +the IMAP provider will issue the PROXYAUTH command with +the user name specified in this property. +
    mail.imap.localaddressString +Local address (host name) to bind to when creating the IMAP socket. +Defaults to the address picked by the Socket class. +Should not normally need to be set, but useful with multi-homed hosts +where it's important to pick a particular local address to bind to. +
    mail.imap.localportint +Local port number to bind to when creating the IMAP socket. +Defaults to the port number picked by the Socket class. +
    mail.imap.sasl.enableboolean +If set to true, attempt to use the javax.security.sasl package to +choose an authentication mechanism for login. +Defaults to false. +
    mail.imap.sasl.mechanismsString +A space or comma separated list of SASL mechanism names to try +to use. +
    mail.imap.sasl.authorizationidString +The authorization ID to use in the SASL authentication. +If not set, the authentication ID (user name) is used. +
    mail.imap.sasl.realmStringThe realm to use with SASL authentication mechanisms that +require a realm, such as DIGEST-MD5.
    mail.imap.sasl.usecanonicalhostnameboolean +If set to true, the canonical host name returned by +{@link java.net.InetAddress#getCanonicalHostName InetAddress.getCanonicalHostName} +is passed to the SASL mechanism, instead of the host name used to connect. +Defaults to false. +
    mail.imap.sasl. xgwtrustedapphack.enableboolean +If set to true, enables a workaround for a bug in the Novell Groupwise +XGWTRUSTEDAPP SASL mechanism, when that mechanism is being used. +Defaults to true. +
    mail.imap.socketFactorySocketFactory +If set to a class that implements the +javax.net.SocketFactory interface, this class +will be used to create IMAP sockets. Note that this is an +instance of a class, not a name, and must be set using the +put method, not the setProperty method. +
    mail.imap.socketFactory.classString +If set, specifies the name of a class that implements the +javax.net.SocketFactory interface. This class +will be used to create IMAP sockets. +
    mail.imap.socketFactory.fallbackboolean +If set to true, failure to create a socket using the specified +socket factory class will cause the socket to be created using +the java.net.Socket class. +Defaults to true. +
    mail.imap.socketFactory.portint +Specifies the port to connect to when using the specified socket +factory. +If not set, the default port will be used. +
    mail.imap.usesocketchannelsboolean +If set to true, use SocketChannels instead of Sockets for connecting +to the server. Required if using the IdleManager. +Ignored if a socket factory is set. +Defaults to false. +
    mail.imap.ssl.enableboolean +If set to true, use SSL to connect and use the SSL port by default. +Defaults to false for the "imap" protocol and true for the "imaps" protocol. +
    mail.imap.ssl.checkserveridentityboolean +If set to true, check the server identity as specified by +RFC 2595. +These additional checks based on the content of the server's certificate +are intended to prevent man-in-the-middle attacks. +Defaults to false. +
    mail.imap.ssl.trustString +If set, and a socket factory hasn't been specified, enables use of a +{@link com.sun.mail.util.MailSSLSocketFactory MailSSLSocketFactory}. +If set to "*", all hosts are trusted. +If set to a whitespace separated list of hosts, those hosts are trusted. +Otherwise, trust depends on the certificate the server presents. +
    mail.imap.ssl.socketFactorySSLSocketFactory +If set to a class that extends the +javax.net.ssl.SSLSocketFactory class, this class +will be used to create IMAP SSL sockets. Note that this is an +instance of a class, not a name, and must be set using the +put method, not the setProperty method. +
    mail.imap.ssl.socketFactory.classString +If set, specifies the name of a class that extends the +javax.net.ssl.SSLSocketFactory class. This class +will be used to create IMAP SSL sockets. +
    mail.imap.ssl.socketFactory.portint +Specifies the port to connect to when using the specified socket +factory. +If not set, the default port will be used. +
    mail.imap.ssl.protocolsstring +Specifies the SSL protocols that will be enabled for SSL connections. +The property value is a whitespace separated list of tokens acceptable +to the javax.net.ssl.SSLSocket.setEnabledProtocols method. +
    mail.imap.ssl.ciphersuitesstring +Specifies the SSL cipher suites that will be enabled for SSL connections. +The property value is a whitespace separated list of tokens acceptable +to the javax.net.ssl.SSLSocket.setEnabledCipherSuites method. +
    mail.imap.starttls.enablebooleanIf true, enables the use of the STARTTLS command (if +supported by the server) to switch the connection to a TLS-protected +connection before issuing any login commands. +If the server does not support STARTTLS, the connection continues without +the use of TLS; see the +mail.imap.starttls.required +property to fail if STARTTLS isn't supported. +Note that an appropriate trust store must configured so that the client +will trust the server's certificate. +Default is false.
    mail.imap.starttls.requiredboolean +If true, requires the use of the STARTTLS command. +If the server doesn't support the STARTTLS command, or the command +fails, the connect method will fail. +Defaults to false. +
    mail.imap.proxy.hoststring +Specifies the host name of an HTTP web proxy server that will be used for +connections to the mail server. +
    mail.imap.proxy.portstring +Specifies the port number for the HTTP web proxy server. +Defaults to port 80. +
    mail.imap.proxy.userstring +Specifies the user name to use to authenticate with the HTTP web proxy server. +By default, no authentication is done. +
    mail.imap.proxy.passwordstring +Specifies the password to use to authenticate with the HTTP web proxy server. +By default, no authentication is done. +
    mail.imap.socks.hoststring +Specifies the host name of a SOCKS5 proxy server that will be used for +connections to the mail server. +
    mail.imap.socks.portstring +Specifies the port number for the SOCKS5 proxy server. +This should only need to be used if the proxy server is not using +the standard port number of 1080. +
    mail.imap.minidletimeint +Applications typically call the idle method in a loop. If another +thread termiantes the IDLE command, it needs a chance to do its +work before another IDLE command is issued. The idle method enforces +a delay to prevent thrashing between the IDLE command and regular +commands. This property sets the delay in milliseconds. If not +set, the default is 10 milliseconds. +
    mail.imap.enableresponseeventsboolean +Enable special IMAP-specific events to be delivered to the Store's +ConnectionListener. If true, IMAP OK, NO, BAD, or BYE responses +will be sent as ConnectionEvents with a type of +IMAPStore.RESPONSE. The event's message will be the +raw IMAP response string. +By default, these events are not sent. +NOTE: This capability is highly experimental and likely will change +in future releases. +
    mail.imap.enableimapeventsboolean +Enable special IMAP-specific events to be delivered to the Store's +ConnectionListener. If true, unsolicited responses +received during the Store's idle method will be sent +as ConnectionEvents with a type of +IMAPStore.RESPONSE. The event's message will be the +raw IMAP response string. +By default, these events are not sent. +NOTE: This capability is highly experimental and likely will change +in future releases. +
    mail.imap.throwsearchexceptionboolean +If set to true and a {@link javax.mail.search.SearchTerm SearchTerm} +passed to the +{@link javax.mail.Folder#search Folder.search} +method is too complex for the IMAP protocol, throw a +{@link javax.mail.search.SearchException SearchException}. +For example, the IMAP protocol only supports less-than and greater-than +comparisons for a {@link javax.mail.search.SizeTerm SizeTerm}. +If false, the search will be done locally by fetching the required +message data and comparing it locally. +Defaults to false. +
    mail.imap.folder.classString +Class name of a subclass of com.sun.mail.imap.IMAPFolder. +The subclass can be used to provide support for additional IMAP commands. +The subclass must have public constructors of the form +public MyIMAPFolder(String fullName, char separator, IMAPStore store, +Boolean isNamespace) and +public MyIMAPFolder(ListInfo li, IMAPStore store) +
    mail.imap.closefoldersonstorefailureboolean +In some cases, a failure of the Store connection indicates a failure of the +server, and all Folders associated with that Store should also be closed. +In other cases, a Store connection failure may be a transient failure, and +Folders may continue to operate normally. +If this property is true (the default), failures in the Store connection cause +all associated Folders to be closed. +Set this property to false to better handle transient failures in the Store +connection. +
    mail.imap.finalizecleancloseboolean +When the finalizer for IMAPStore is called, +should the connection to the server be closed cleanly, as if the +application called the close method? +Or should the connection to the server be closed without sending +any commands to the server? +Defaults to false, the connection is not closed cleanly. +
    mail.imap.referralexceptionboolean +If set to true and an IMAP login referral is returned when the authentication +succeeds, fail the connect request and throw a +{@link com.sun.mail.imap.ReferralException ReferralException}. +Defaults to false. +
    mail.imap.compress.enableboolean +If set to true and the IMAP server supports the COMPRESS=DEFLATE extension, +compression will be enabled. +Defaults to false. +
    mail.imap.compress.levelint +The compression level to be used, in the range -1 to 9. +See the {@link java.util.zip.Deflater Deflater} class for details. +
    mail.imap.compress.strategyint +The compression strategy to be used, in the range 0 to 2. +See the {@link java.util.zip.Deflater Deflater} class for details. +
    mail.imap.compress.strategyboolean +If true, always use "A" for the IMAP command tag prefix. +If false, the IMAP command tag prefix is different for each connection, +from "A" through "ZZZ" and then wrapping around to "A". +Applications should never need to set this. +Defaults to false. +
    +

    +In general, applications should not need to use the classes in this +package directly. Instead, they should use the APIs defined by the +javax.mail package (and subpackages). Applications should +never construct instances of IMAPStore or +IMAPFolder directly. Instead, they should use the +Session method getStore to acquire an +appropriate Store object, and from that acquire +Folder objects. +

    +Loggers +

    +In addition to printing debugging output as controlled by the +{@link javax.mail.Session Session} configuration, +the com.sun.mail.imap provider logs the same information using +{@link java.util.logging.Logger} as described in the following table: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Logger NameLogging LevelPurpose
    com.sun.mail.imapCONFIGConfiguration of the IMAPStore
    com.sun.mail.imapFINEGeneral debugging output
    com.sun.mail.imap.connectionpoolCONFIGConfiguration of the IMAP connection pool
    com.sun.mail.imap.connectionpoolFINEDebugging output related to the IMAP connection pool
    com.sun.mail.imap.messagecacheCONFIGConfiguration of the IMAP message cache
    com.sun.mail.imap.messagecacheFINEDebugging output related to the IMAP message cache
    com.sun.mail.imap.protocolFINESTComplete protocol trace
    + +WARNING +

    +WARNING: The APIs unique to this package should be +considered EXPERIMENTAL. They may be changed in the +future in ways that are incompatible with applications using the +current APIs. +

    + + + diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BASE64MailboxDecoder.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BASE64MailboxDecoder.java new file mode 100644 index 000000000..3c5a58a71 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BASE64MailboxDecoder.java @@ -0,0 +1,194 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.text.StringCharacterIterator; +import java.text.CharacterIterator; + +/** + * See the BASE64MailboxEncoder for a description of the RFC2060 and how + * mailbox names should be encoded. This class will do the correct decoding + * for mailbox names. + * + * @author Christopher Cotton + */ + +public class BASE64MailboxDecoder { + + public static String decode(String original) { + if (original == null || original.length() == 0) + return original; + + boolean changedString = false; + int copyTo = 0; + // it will always be less than the original + char[] chars = new char[original.length()]; + StringCharacterIterator iter = new StringCharacterIterator(original); + + for(char c = iter.first(); c != CharacterIterator.DONE; + c = iter.next()) { + + if (c == '&') { + changedString = true; + copyTo = base64decode(chars, copyTo, iter); + } else { + chars[copyTo++] = c; + } + } + + // now create our string from the char array + if (changedString) { + return new String(chars, 0, copyTo); + } else { + return original; + } + } + + + protected static int base64decode(char[] buffer, int offset, + CharacterIterator iter) { + boolean firsttime = true; + int leftover = -1; + + while(true) { + // get the first byte + byte orig_0 = (byte) iter.next(); + if (orig_0 == -1) break; // no more chars + if (orig_0 == '-') { + if (firsttime) { + // means we got the string "&-" which is turned into a "&" + buffer[offset++] = '&'; + } + // we are done now + break; + } + firsttime = false; + + // next byte + byte orig_1 = (byte) iter.next(); + if (orig_1 == -1 || orig_1 == '-') + break; // no more chars, invalid base64 + + byte a, b, current; + a = pem_convert_array[orig_0 & 0xff]; + b = pem_convert_array[orig_1 & 0xff]; + // The first decoded byte + current = (byte)(((a << 2) & 0xfc) | ((b >>> 4) & 3)); + + // use the leftover to create a Unicode Character (2 bytes) + if (leftover != -1) { + buffer[offset++] = (char)(leftover << 8 | (current & 0xff)); + leftover = -1; + } else { + leftover = current & 0xff; + } + + byte orig_2 = (byte) iter.next(); + if (orig_2 == '=') { // End of this BASE64 encoding + continue; + } else if (orig_2 == -1 || orig_2 == '-') { + break; // no more chars + } + + // second decoded byte + a = b; + b = pem_convert_array[orig_2 & 0xff]; + current = (byte)(((a << 4) & 0xf0) | ((b >>> 2) & 0xf)); + + // use the leftover to create a Unicode Character (2 bytes) + if (leftover != -1) { + buffer[offset++] = (char)(leftover << 8 | (current & 0xff)); + leftover = -1; + } else { + leftover = current & 0xff; + } + + byte orig_3 = (byte) iter.next(); + if (orig_3 == '=') { // End of this BASE64 encoding + continue; + } else if (orig_3 == -1 || orig_3 == '-') { + break; // no more chars + } + + // The third decoded byte + a = b; + b = pem_convert_array[orig_3 & 0xff]; + current = (byte)(((a << 6) & 0xc0) | (b & 0x3f)); + + // use the leftover to create a Unicode Character (2 bytes) + if (leftover != -1) { + buffer[offset++] = (char)(leftover << 8 | (current & 0xff)); + leftover = -1; + } else { + leftover = current & 0xff; + } + } + + return offset; + } + + /** + * This character array provides the character to value map + * based on RFC1521, but with the modification from RFC2060 + * which changes the '/' to a ','. + */ + + // shared with BASE64MailboxEncoder + static final char pem_array[] = { + 'A','B','C','D','E','F','G','H', // 0 + 'I','J','K','L','M','N','O','P', // 1 + 'Q','R','S','T','U','V','W','X', // 2 + 'Y','Z','a','b','c','d','e','f', // 3 + 'g','h','i','j','k','l','m','n', // 4 + 'o','p','q','r','s','t','u','v', // 5 + 'w','x','y','z','0','1','2','3', // 6 + '4','5','6','7','8','9','+',',' // 7 + }; + + private static final byte pem_convert_array[] = new byte[256]; + + static { + for (int i = 0; i < 255; i++) + pem_convert_array[i] = -1; + for(int i = 0; i < pem_array.length; i++) + pem_convert_array[pem_array[i]] = (byte) i; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BASE64MailboxEncoder.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BASE64MailboxEncoder.java new file mode 100644 index 000000000..e31d321b1 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BASE64MailboxEncoder.java @@ -0,0 +1,254 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.io.*; + + +/** + * From RFC2060: + * + *
    + *
    + * 5.1.3.  Mailbox International Naming Convention
    + *
    + *   By convention, international mailbox names are specified using a
    + *   modified version of the UTF-7 encoding described in [UTF-7].  The
    + *   purpose of these modifications is to correct the following problems
    + *   with UTF-7:
    + *
    + *      1) UTF-7 uses the "+" character for shifting; this conflicts with
    + *         the common use of "+" in mailbox names, in particular USENET
    + *         newsgroup names.
    + *
    + *      2) UTF-7's encoding is BASE64 which uses the "/" character; this
    + *         conflicts with the use of "/" as a popular hierarchy delimiter.
    + *
    + *      3) UTF-7 prohibits the unencoded usage of "\"; this conflicts with
    + *         the use of "\" as a popular hierarchy delimiter.
    + *
    + *      4) UTF-7 prohibits the unencoded usage of "~"; this conflicts with
    + *         the use of "~" in some servers as a home directory indicator.
    + *
    + *      5) UTF-7 permits multiple alternate forms to represent the same
    + *         string; in particular, printable US-ASCII chararacters can be
    + *         represented in encoded form.
    + *
    + *   In modified UTF-7, printable US-ASCII characters except for "&"
    + *   represent themselves; that is, characters with octet values 0x20-0x25
    + *   and 0x27-0x7e.  The character "&" (0x26) is represented by the two-
    + *   octet sequence "&-".
    + *
    + *   All other characters (octet values 0x00-0x1f, 0x7f-0xff, and all
    + *   Unicode 16-bit octets) are represented in modified BASE64, with a
    + *   further modification from [UTF-7] that "," is used instead of "/".
    + *   Modified BASE64 MUST NOT be used to represent any printing US-ASCII
    + *   character which can represent itself.
    + *
    + *   "&" is used to shift to modified BASE64 and "-" to shift back to US-
    + *   ASCII.  All names start in US-ASCII, and MUST end in US-ASCII (that
    + *   is, a name that ends with a Unicode 16-bit octet MUST end with a "-
    + *   ").
    + *
    + *   For example, here is a mailbox name which mixes English, Japanese,
    + *   and Chinese text: ~peter/mail/&ZeVnLIqe-/&U,BTFw-
    + *
    + * 
    + * + * This class will do the correct Encoding for the IMAP mailboxes. + * + * @author Christopher Cotton + */ + +public class BASE64MailboxEncoder { + protected byte[] buffer = new byte[4]; + protected int bufsize = 0; + protected boolean started = false; + protected Writer out = null; + + + public static String encode(String original) { + BASE64MailboxEncoder base64stream = null; + char origchars[] = original.toCharArray(); + int length = origchars.length; + boolean changedString = false; + CharArrayWriter writer = new CharArrayWriter(length); + + // loop over all the chars + for(int index = 0; index < length; index++) { + char current = origchars[index]; + + // octets in the range 0x20-0x25,0x27-0x7e are themselves + // 0x26 "&" is represented as "&-" + if (current >= 0x20 && current <= 0x7e) { + if (base64stream != null) { + base64stream.flush(); + } + + if (current == '&') { + changedString = true; + writer.write('&'); + writer.write('-'); + } else { + writer.write(current); + } + } else { + + // use a B64MailboxEncoder to write out the other bytes + // as a modified BASE64. The stream will write out + // the beginning '&' and the ending '-' which is part + // of every encoding. + + if (base64stream == null) { + base64stream = new BASE64MailboxEncoder(writer); + changedString = true; + } + + base64stream.write(current); + } + } + + + if (base64stream != null) { + base64stream.flush(); + } + + if (changedString) { + return writer.toString(); + } else { + return original; + } + } + + + /** + * Create a BASE64 encoder + * + * @param what where to write the encoded name + */ + public BASE64MailboxEncoder(Writer what) { + out = what; + } + + public void write(int c) { + try { + // write out the initial character if this is the first time + if (!started) { + started = true; + out.write('&'); + } + + // we write each character as a 2 byte unicode character + buffer[bufsize++] = (byte) (c >> 8); + buffer[bufsize++] = (byte) (c & 0xff); + + if (bufsize >= 3) { + encode(); + bufsize -= 3; + } + } catch (IOException e) { + //e.printStackTrace(); + } + } + + + public void flush() { + try { + // flush any bytes we have + if (bufsize > 0) { + encode(); + bufsize = 0; + } + + // write the terminating character of the encoding + if (started) { + out.write('-'); + started = false; + } + } catch (IOException e) { + //e.printStackTrace(); + } + } + + + protected void encode() throws IOException { + byte a, b, c; + if (bufsize == 1) { + a = buffer[0]; + b = 0; + c = 0; + out.write(pem_array[(a >>> 2) & 0x3F]); + out.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); + // no padding characters are written + } else if (bufsize == 2) { + a = buffer[0]; + b = buffer[1]; + c = 0; + out.write(pem_array[(a >>> 2) & 0x3F]); + out.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); + out.write(pem_array[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]); + // no padding characters are written + } else { + a = buffer[0]; + b = buffer[1]; + c = buffer[2]; + out.write(pem_array[(a >>> 2) & 0x3F]); + out.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); + out.write(pem_array[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]); + out.write(pem_array[c & 0x3F]); + + // copy back the extra byte + if (bufsize == 4) + buffer[0] = buffer[3]; + } + } + + private final static char pem_array[] = { + 'A','B','C','D','E','F','G','H', // 0 + 'I','J','K','L','M','N','O','P', // 1 + 'Q','R','S','T','U','V','W','X', // 2 + 'Y','Z','a','b','c','d','e','f', // 3 + 'g','h','i','j','k','l','m','n', // 4 + 'o','p','q','r','s','t','u','v', // 5 + 'w','x','y','z','0','1','2','3', // 6 + '4','5','6','7','8','9','+',',' // 7 + }; +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BODY.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BODY.java new file mode 100644 index 000000000..406846e64 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BODY.java @@ -0,0 +1,111 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.io.ByteArrayInputStream; +import com.sun.mail.iap.*; +import com.sun.mail.util.ASCIIUtility; + +/** + * The BODY fetch response item. + * + * @author John Mani + * @author Bill Shannon + */ + +public class BODY implements Item { + + static final char[] name = {'B','O','D','Y'}; + + private final int msgno; + private final ByteArray data; + private final String section; + private final int origin; + private final boolean isHeader; + + /** + * Constructor + * + * @param r the FetchResponse + * @exception ParsingException for parsing failures + */ + public BODY(FetchResponse r) throws ParsingException { + msgno = r.getNumber(); + + r.skipSpaces(); + + if (r.readByte() != '[') + throw new ParsingException( + "BODY parse error: missing ``['' at section start"); + section = r.readString(']'); + if (r.readByte() != ']') + throw new ParsingException( + "BODY parse error: missing ``]'' at section end"); + isHeader = section.regionMatches(true, 0, "HEADER", 0, 6); + + if (r.readByte() == '<') { // origin + origin = r.readNumber(); + r.skip(1); // skip '>'; + } else + origin = 0; + + data = r.readByteArray(); + } + + public ByteArray getByteArray() { + return data; + } + + public ByteArrayInputStream getByteArrayInputStream() { + if (data != null) + return data.toByteArrayInputStream(); + else + return null; + } + + public boolean isHeader() { + return isHeader; + } + + public String getSection() { + return section; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BODYSTRUCTURE.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BODYSTRUCTURE.java new file mode 100644 index 000000000..0c4d7fcf4 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/BODYSTRUCTURE.java @@ -0,0 +1,474 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.List; +import java.util.ArrayList; +import javax.mail.internet.ParameterList; +import com.sun.mail.iap.*; +import com.sun.mail.util.PropUtil; + +/** + * A BODYSTRUCTURE response. + * + * @author John Mani + * @author Bill Shannon + */ + +public class BODYSTRUCTURE implements Item { + + static final char[] name = + {'B','O','D','Y','S','T','R','U','C','T','U','R','E'}; + public int msgno; + + public String type; // Type + public String subtype; // Subtype + public String encoding; // Encoding + public int lines = -1; // Size in lines + public int size = -1; // Size in bytes + public String disposition; // Disposition + public String id; // Content-ID + public String description; // Content-Description + public String md5; // MD-5 checksum + public String attachment; // Attachment name + public ParameterList cParams; // Body parameters + public ParameterList dParams; // Disposition parameters + public String[] language; // Language + public BODYSTRUCTURE[] bodies; // array of BODYSTRUCTURE objects + // for multipart & message/rfc822 + public ENVELOPE envelope; // for message/rfc822 + + private static int SINGLE = 1; + private static int MULTI = 2; + private static int NESTED = 3; + private int processedType; // MULTI | SINGLE | NESTED + + // special debugging output to debug parsing errors + private static final boolean parseDebug = + PropUtil.getBooleanSystemProperty("mail.imap.parse.debug", false); + + + public BODYSTRUCTURE(FetchResponse r) throws ParsingException { + if (parseDebug) + System.out.println("DEBUG IMAP: parsing BODYSTRUCTURE"); + msgno = r.getNumber(); + if (parseDebug) + System.out.println("DEBUG IMAP: msgno " + msgno); + + r.skipSpaces(); + + if (r.readByte() != '(') + throw new ParsingException( + "BODYSTRUCTURE parse error: missing ``('' at start"); + + if (r.peekByte() == '(') { // multipart + if (parseDebug) + System.out.println("DEBUG IMAP: parsing multipart"); + type = "multipart"; + processedType = MULTI; + List v = new ArrayList<>(1); + int i = 1; + do { + v.add(new BODYSTRUCTURE(r)); + /* + * Even though the IMAP spec says there can't be any spaces + * between parts, some servers erroneously put a space in + * here. In the spirit of "be liberal in what you accept", + * we skip it. + */ + r.skipSpaces(); + } while (r.peekByte() == '('); + + // setup bodies. + bodies = v.toArray(new BODYSTRUCTURE[v.size()]); + + subtype = r.readString(); // subtype + if (parseDebug) + System.out.println("DEBUG IMAP: subtype " + subtype); + + if (r.isNextNonSpace(')')) { // done + if (parseDebug) + System.out.println("DEBUG IMAP: parse DONE"); + return; + } + + // Else, we have extension data + + if (parseDebug) + System.out.println("DEBUG IMAP: parsing extension data"); + // Body parameters + cParams = parseParameters(r); + if (r.isNextNonSpace(')')) { // done + if (parseDebug) + System.out.println("DEBUG IMAP: body parameters DONE"); + return; + } + + // Disposition + byte b = r.peekByte(); + if (b == '(') { + if (parseDebug) + System.out.println("DEBUG IMAP: parse disposition"); + r.readByte(); + disposition = r.readString(); + if (parseDebug) + System.out.println("DEBUG IMAP: disposition " + + disposition); + dParams = parseParameters(r); + if (!r.isNextNonSpace(')')) // eat the end ')' + throw new ParsingException( + "BODYSTRUCTURE parse error: " + + "missing ``)'' at end of disposition in multipart"); + if (parseDebug) + System.out.println("DEBUG IMAP: disposition DONE"); + } else if (b == 'N' || b == 'n') { + if (parseDebug) + System.out.println("DEBUG IMAP: disposition NIL"); + r.skip(3); // skip 'NIL' + } else { + /* + throw new ParsingException( + "BODYSTRUCTURE parse error: " + + type + "/" + subtype + ": " + + "bad multipart disposition, b " + b); + */ + if (parseDebug) + System.out.println("DEBUG IMAP: bad multipart disposition" + + ", applying Exchange bug workaround"); + description = r.readString(); + if (parseDebug) + System.out.println("DEBUG IMAP: multipart description " + + description); + // Throw away whatever comes after it, since we have no + // idea what it's supposed to be + while (r.readByte() == ' ') + parseBodyExtension(r); + return; + } + + // RFC3501 allows no body-fld-lang after body-fld-disp, + // even though RFC2060 required it + if (r.isNextNonSpace(')')) { + if (parseDebug) + System.out.println("DEBUG IMAP: no body-fld-lang"); + return; // done + } + + // Language + if (r.peekByte() == '(') { // a list follows + language = r.readStringList(); + if (parseDebug) + System.out.println( + "DEBUG IMAP: language len " + language.length); + } else { + String l = r.readString(); + if (l != null) { + String[] la = { l }; + language = la; + if (parseDebug) + System.out.println("DEBUG IMAP: language " + l); + } + } + + // RFC3501 defines an optional "body location" next, + // but for now we ignore it along with other extensions. + + // Throw away any further extension data + while (r.readByte() == ' ') + parseBodyExtension(r); + } else if (r.peekByte() == ')') { // (illegal) empty body + /* + * Domino will fail to return the body structure of nested messages. + * Fake it by providing an empty message. Could probably do better + * with more work... + */ + /* + * XXX - this prevents the exception, but without the exception + * the application has no way to know the data from the message + * is missing. + * + if (parseDebug) + System.out.println("DEBUG IMAP: empty body, fake it"); + r.readByte(); + type = "text"; + subtype = "plain"; + lines = 0; + size = 0; + */ + throw new ParsingException( + "BODYSTRUCTURE parse error: missing body content"); + } else { // Single part + if (parseDebug) + System.out.println("DEBUG IMAP: single part"); + type = r.readString(); + if (parseDebug) + System.out.println("DEBUG IMAP: type " + type); + processedType = SINGLE; + subtype = r.readString(); + if (parseDebug) + System.out.println("DEBUG IMAP: subtype " + subtype); + + // SIMS 4.0 returns NIL for a Content-Type of "binary", fix it here + if (type == null) { + type = "application"; + subtype = "octet-stream"; + } + cParams = parseParameters(r); + if (parseDebug) + System.out.println("DEBUG IMAP: cParams " + cParams); + id = r.readString(); + if (parseDebug) + System.out.println("DEBUG IMAP: id " + id); + description = r.readString(); + if (parseDebug) + System.out.println("DEBUG IMAP: description " + description); + /* + * XXX - Work around bug in Exchange 2010 that + * returns unquoted string. + */ + encoding = r.readAtomString(); + if (encoding != null && encoding.equalsIgnoreCase("NIL")) { + if (parseDebug) + System.out.println("DEBUG IMAP: NIL encoding" + + ", applying Exchange bug workaround"); + encoding = null; + } + /* + * XXX - Work around bug in office365.com that returns + * a string with a trailing space in some cases. + */ + if (encoding != null) + encoding = encoding.trim(); + if (parseDebug) + System.out.println("DEBUG IMAP: encoding " + encoding); + size = r.readNumber(); + if (parseDebug) + System.out.println("DEBUG IMAP: size " + size); + if (size < 0) + throw new ParsingException( + "BODYSTRUCTURE parse error: bad ``size'' element"); + + // "text/*" & "message/rfc822" types have additional data .. + if (type.equalsIgnoreCase("text")) { + lines = r.readNumber(); + if (parseDebug) + System.out.println("DEBUG IMAP: lines " + lines); + if (lines < 0) + throw new ParsingException( + "BODYSTRUCTURE parse error: bad ``lines'' element"); + } else if (type.equalsIgnoreCase("message") && + subtype.equalsIgnoreCase("rfc822")) { + // Nested message + processedType = NESTED; + // The envelope comes next, but sadly Gmail handles nested + // messages just like simple body parts and fails to return + // the envelope and body structure of the message (sort of + // like IMAP4 before rev1). + r.skipSpaces(); + if (r.peekByte() == '(') { // the envelope follows + envelope = new ENVELOPE(r); + if (parseDebug) + System.out.println( + "DEBUG IMAP: got envelope of nested message"); + BODYSTRUCTURE[] bs = { new BODYSTRUCTURE(r) }; + bodies = bs; + lines = r.readNumber(); + if (parseDebug) + System.out.println("DEBUG IMAP: lines " + lines); + if (lines < 0) + throw new ParsingException( + "BODYSTRUCTURE parse error: bad ``lines'' element"); + } else { + if (parseDebug) + System.out.println("DEBUG IMAP: " + + "missing envelope and body of nested message"); + } + } else { + // Detect common error of including lines element on other types + r.skipSpaces(); + byte bn = r.peekByte(); + if (Character.isDigit((char)bn)) // number + throw new ParsingException( + "BODYSTRUCTURE parse error: server erroneously " + + "included ``lines'' element with type " + + type + "/" + subtype); + } + + if (r.isNextNonSpace(')')) { + if (parseDebug) + System.out.println("DEBUG IMAP: parse DONE"); + return; // done + } + + // Optional extension data + + // MD5 + md5 = r.readString(); + if (r.isNextNonSpace(')')) { + if (parseDebug) + System.out.println("DEBUG IMAP: no MD5 DONE"); + return; // done + } + + // Disposition + byte b = r.readByte(); + if (b == '(') { + disposition = r.readString(); + if (parseDebug) + System.out.println("DEBUG IMAP: disposition " + + disposition); + dParams = parseParameters(r); + if (parseDebug) + System.out.println("DEBUG IMAP: dParams " + dParams); + if (!r.isNextNonSpace(')')) // eat the end ')' + throw new ParsingException( + "BODYSTRUCTURE parse error: " + + "missing ``)'' at end of disposition"); + } else if (b == 'N' || b == 'n') { + if (parseDebug) + System.out.println("DEBUG IMAP: disposition NIL"); + r.skip(2); // skip 'NIL' + } else { + throw new ParsingException( + "BODYSTRUCTURE parse error: " + + type + "/" + subtype + ": " + + "bad single part disposition, b " + b); + } + + if (r.isNextNonSpace(')')) { + if (parseDebug) + System.out.println("DEBUG IMAP: disposition DONE"); + return; // done + } + + // Language + if (r.peekByte() == '(') { // a list follows + language = r.readStringList(); + if (parseDebug) + System.out.println("DEBUG IMAP: language len " + + language.length); + } else { // protocol is unnessarily complex here + String l = r.readString(); + if (l != null) { + String[] la = { l }; + language = la; + if (parseDebug) + System.out.println("DEBUG IMAP: language " + l); + } + } + + // RFC3501 defines an optional "body location" next, + // but for now we ignore it along with other extensions. + + // Throw away any further extension data + while (r.readByte() == ' ') + parseBodyExtension(r); + if (parseDebug) + System.out.println("DEBUG IMAP: all DONE"); + } + } + + public boolean isMulti() { + return processedType == MULTI; + } + + public boolean isSingle() { + return processedType == SINGLE; + } + + public boolean isNested() { + return processedType == NESTED; + } + + private ParameterList parseParameters(Response r) + throws ParsingException { + r.skipSpaces(); + + ParameterList list = null; + byte b = r.readByte(); + if (b == '(') { + list = new ParameterList(); + do { + String name = r.readString(); + if (parseDebug) + System.out.println("DEBUG IMAP: parameter name " + name); + if (name == null) + throw new ParsingException( + "BODYSTRUCTURE parse error: " + + type + "/" + subtype + ": " + + "null name in parameter list"); + String value = r.readString(); + if (parseDebug) + System.out.println("DEBUG IMAP: parameter value " + value); + if (value == null) { // work around buggy servers + if (parseDebug) + System.out.println("DEBUG IMAP: NIL parameter value" + + ", applying Exchange bug workaround"); + value = ""; + } + list.set(name, value); + } while (!r.isNextNonSpace(')')); + list.combineSegments(); + } else if (b == 'N' || b == 'n') { + if (parseDebug) + System.out.println("DEBUG IMAP: parameter list NIL"); + r.skip(2); + } else + throw new ParsingException("Parameter list parse error"); + + return list; + } + + private void parseBodyExtension(Response r) throws ParsingException { + r.skipSpaces(); + + byte b = r.peekByte(); + if (b == '(') { + r.skip(1); // skip '(' + do { + parseBodyExtension(r); + } while (!r.isNextNonSpace(')')); + } else if (Character.isDigit((char)b)) // number + r.readNumber(); + else // nstring + r.readString(); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/ENVELOPE.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/ENVELOPE.java new file mode 100644 index 000000000..1544accf4 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/ENVELOPE.java @@ -0,0 +1,248 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.List; +import java.util.ArrayList; +import java.util.Date; +import java.io.UnsupportedEncodingException; +import java.text.ParseException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.AddressException; +import javax.mail.internet.MailDateFormat; +import javax.mail.internet.MimeUtility; +import com.sun.mail.iap.*; +import com.sun.mail.util.PropUtil; + +/** + * The ENEVELOPE item of an IMAP FETCH response. + * + * @author John Mani + * @author Bill Shannon + */ + +public class ENVELOPE implements Item { + + // IMAP item name + static final char[] name = {'E','N','V','E','L','O','P','E'}; + public int msgno; + + public Date date = null; + public String subject; + public InternetAddress[] from; + public InternetAddress[] sender; + public InternetAddress[] replyTo; + public InternetAddress[] to; + public InternetAddress[] cc; + public InternetAddress[] bcc; + public String inReplyTo; + public String messageId; + + // Used to parse dates + private static final MailDateFormat mailDateFormat = new MailDateFormat(); + + // special debugging output to debug parsing errors + private static final boolean parseDebug = + PropUtil.getBooleanSystemProperty("mail.imap.parse.debug", false); + + public ENVELOPE(FetchResponse r) throws ParsingException { + if (parseDebug) + System.out.println("parse ENVELOPE"); + msgno = r.getNumber(); + + r.skipSpaces(); + + if (r.readByte() != '(') + throw new ParsingException("ENVELOPE parse error"); + + String s = r.readString(); + if (s != null) { + try { + synchronized (mailDateFormat) { + date = mailDateFormat.parse(s); + } + } catch (ParseException pex) { + } + } + if (parseDebug) + System.out.println(" Date: " + date); + + subject = r.readString(); + if (parseDebug) + System.out.println(" Subject: " + subject); + if (parseDebug) + System.out.println(" From addresses:"); + from = parseAddressList(r); + if (parseDebug) + System.out.println(" Sender addresses:"); + sender = parseAddressList(r); + if (parseDebug) + System.out.println(" Reply-To addresses:"); + replyTo = parseAddressList(r); + if (parseDebug) + System.out.println(" To addresses:"); + to = parseAddressList(r); + if (parseDebug) + System.out.println(" Cc addresses:"); + cc = parseAddressList(r); + if (parseDebug) + System.out.println(" Bcc addresses:"); + bcc = parseAddressList(r); + inReplyTo = r.readString(); + if (parseDebug) + System.out.println(" In-Reply-To: " + inReplyTo); + messageId = r.readString(); + if (parseDebug) + System.out.println(" Message-ID: " + messageId); + + if (!r.isNextNonSpace(')')) + throw new ParsingException("ENVELOPE parse error"); + } + + private InternetAddress[] parseAddressList(Response r) + throws ParsingException { + r.skipSpaces(); // skip leading spaces + + byte b = r.readByte(); + if (b == '(') { + /* + * Some broken servers (e.g., Yahoo Mail) return an empty + * list instead of NIL. Handle that here even though it + * doesn't conform to the IMAP spec. + */ + if (r.isNextNonSpace(')')) + return null; + + List v = new ArrayList<>(); + + do { + IMAPAddress a = new IMAPAddress(r); + if (parseDebug) + System.out.println(" Address: " + a); + // if we see an end-of-group address at the top, ignore it + if (!a.isEndOfGroup()) + v.add(a); + } while (!r.isNextNonSpace(')')); + + return v.toArray(new InternetAddress[v.size()]); + } else if (b == 'N' || b == 'n') { // NIL + r.skip(2); // skip 'NIL' + return null; + } else + throw new ParsingException("ADDRESS parse error"); + } +} + +class IMAPAddress extends InternetAddress { + private boolean group = false; + private InternetAddress[] grouplist; + private String groupname; + + private static final long serialVersionUID = -3835822029483122232L; + + IMAPAddress(Response r) throws ParsingException { + r.skipSpaces(); // skip leading spaces + + if (r.readByte() != '(') + throw new ParsingException("ADDRESS parse error"); + + encodedPersonal = r.readString(); + + r.readString(); // throw away address_list + String mb = r.readString(); + String host = r.readString(); + // skip bogus spaces inserted by Yahoo IMAP server if + // "undisclosed-recipients" is a recipient + r.skipSpaces(); + if (!r.isNextNonSpace(')')) // skip past terminating ')' + throw new ParsingException("ADDRESS parse error"); + + if (host == null) { + // it's a group list, start or end + group = true; + groupname = mb; + if (groupname == null) // end of group list + return; + // Accumulate a group list. The members of the group + // are accumulated in a List and the corresponding string + // representation of the group is accumulated in a StringBuilder. + StringBuilder sb = new StringBuilder(); + sb.append(groupname).append(':'); + List v = new ArrayList<>(); + while (r.peekByte() != ')') { + IMAPAddress a = new IMAPAddress(r); + if (a.isEndOfGroup()) // reached end of group + break; + if (v.size() != 0) // if not first element, need a comma + sb.append(','); + sb.append(a.toString()); + v.add(a); + } + sb.append(';'); + address = sb.toString(); + grouplist = v.toArray(new IMAPAddress[v.size()]); + } else { + if (mb == null || mb.length() == 0) + address = host; + else if (host.length() == 0) + address = mb; + else + address = mb + "@" + host; + } + + } + + boolean isEndOfGroup() { + return group && groupname == null; + } + + @Override + public boolean isGroup() { + return group; + } + + @Override + public InternetAddress[] getGroup(boolean strict) throws AddressException { + if (grouplist == null) + return null; + return grouplist.clone(); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/FLAGS.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/FLAGS.java new file mode 100644 index 000000000..dc4c298c3 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/FLAGS.java @@ -0,0 +1,110 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import javax.mail.Flags; +import com.sun.mail.iap.*; + +/** + * This class + * + * @author John Mani + */ + +public class FLAGS extends Flags implements Item { + + // IMAP item name + static final char[] name = {'F','L','A','G','S'}; + public int msgno; + + private static final long serialVersionUID = 439049847053756670L; + + /** + * Constructor. + * + * @param r the IMAPResponse + * @exception ParsingException for parsing failures + */ + public FLAGS(IMAPResponse r) throws ParsingException { + msgno = r.getNumber(); + + r.skipSpaces(); + String[] flags = r.readSimpleList(); + if (flags != null) { // if not empty flaglist + for (int i = 0; i < flags.length; i++) { + String s = flags[i]; + if (s.length() >= 2 && s.charAt(0) == '\\') { + switch (Character.toUpperCase(s.charAt(1))) { + case 'S': // \Seen + add(Flags.Flag.SEEN); + break; + case 'R': // \Recent + add(Flags.Flag.RECENT); + break; + case 'D': + if (s.length() >= 3) { + char c = s.charAt(2); + if (c == 'e' || c == 'E') // \Deleted + add(Flags.Flag.DELETED); + else if (c == 'r' || c == 'R') // \Draft + add(Flags.Flag.DRAFT); + } else + add(s); // unknown, treat it as a user flag + break; + case 'A': // \Answered + add(Flags.Flag.ANSWERED); + break; + case 'F': // \Flagged + add(Flags.Flag.FLAGGED); + break; + case '*': // \* + add(Flags.Flag.USER); + break; + default: + add(s); // unknown, treat it as a user flag + break; + } + } else + add(s); + } + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/FetchItem.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/FetchItem.java new file mode 100644 index 000000000..69e090266 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/FetchItem.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.lang.reflect.*; + +import javax.mail.FetchProfile; +import com.sun.mail.iap.ParsingException; + +/** + * Metadata describing a FETCH item. + * Note that the "name" field MUST be in uppercase.

    + * + * @author Bill Shannon + * @since JavaMail 1.4.6 + */ + +public abstract class FetchItem { + private String name; + private FetchProfile.Item fetchProfileItem; + + public FetchItem(String name, FetchProfile.Item fetchProfileItem) { + this.name = name; + this.fetchProfileItem = fetchProfileItem; + } + + public String getName() { + return name; + } + + public FetchProfile.Item getFetchProfileItem() { + return fetchProfileItem; + } + + /** + * Parse the item into some kind of object appropriate for the item. + * Note that the item name will have been parsed and skipped already. + * + * @param r the response + * @return the fetch item + * @exception ParsingException for parsing failures + */ + public abstract Object parseItem(FetchResponse r) throws ParsingException; +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/FetchResponse.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/FetchResponse.java new file mode 100644 index 000000000..fe42ed77e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/FetchResponse.java @@ -0,0 +1,344 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.io.*; +import java.util.*; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.iap.*; + +/** + * This class represents a FETCH response obtained from the input stream + * of an IMAP server. + * + * @author John Mani + * @author Bill Shannon + */ + +public class FetchResponse extends IMAPResponse { + /* + * Regular Items are saved in the items array. + * Extension items (items handled by subclasses + * that extend the IMAP provider) are saved in the + * extensionItems map, indexed by the FETCH item name. + * The map is only created when needed. + * + * XXX - Should consider unifying the handling of + * regular items and extension items. + */ + private Item[] items; + private Map extensionItems; + private final FetchItem[] fitems; + + public FetchResponse(Protocol p) + throws IOException, ProtocolException { + super(p); + fitems = null; + parse(); + } + + public FetchResponse(IMAPResponse r) + throws IOException, ProtocolException { + this(r, null); + } + + /** + * Construct a FetchResponse that handles the additional FetchItems. + * + * @param r the IMAPResponse + * @param fitems the fetch items + * @exception IOException for I/O errors + * @exception ProtocolException for protocol failures + * @since JavaMail 1.4.6 + */ + public FetchResponse(IMAPResponse r, FetchItem[] fitems) + throws IOException, ProtocolException { + super(r); + this.fitems = fitems; + parse(); + } + + public int getItemCount() { + return items.length; + } + + public Item getItem(int index) { + return items[index]; + } + + public T getItem(Class c) { + for (int i = 0; i < items.length; i++) { + if (c.isInstance(items[i])) + return c.cast(items[i]); + } + + return null; + } + + /** + * Return the first fetch response item of the given class + * for the given message number. + * + * @param r the responses + * @param msgno the message number + * @param c the class + * @param the type of fetch item + * @return the fetch item + */ + public static T getItem(Response[] r, int msgno, + Class c) { + if (r == null) + return null; + + for (int i = 0; i < r.length; i++) { + + if (r[i] == null || + !(r[i] instanceof FetchResponse) || + ((FetchResponse)r[i]).getNumber() != msgno) + continue; + + FetchResponse f = (FetchResponse)r[i]; + for (int j = 0; j < f.items.length; j++) { + if (c.isInstance(f.items[j])) + return c.cast(f.items[j]); + } + } + + return null; + } + + /** + * Return all fetch response items of the given class + * for the given message number. + * + * @param r the responses + * @param msgno the message number + * @param c the class + * @param the type of fetch items + * @return the list of fetch items + * @since JavaMail 1.5.2 + */ + public static List getItems(Response[] r, int msgno, + Class c) { + List items = new ArrayList<>(); + + if (r == null) + return items; + + for (int i = 0; i < r.length; i++) { + + if (r[i] == null || + !(r[i] instanceof FetchResponse) || + ((FetchResponse)r[i]).getNumber() != msgno) + continue; + + FetchResponse f = (FetchResponse)r[i]; + for (int j = 0; j < f.items.length; j++) { + if (c.isInstance(f.items[j])) + items.add(c.cast(f.items[j])); + } + } + + return items; + } + + /** + * Return a map of the extension items found in this fetch response. + * The map is indexed by extension item name. Callers should not + * modify the map. + * + * @return Map of extension items, or null if none + * @since JavaMail 1.4.6 + */ + public Map getExtensionItems() { + return extensionItems; + } + + private final static char[] HEADER = {'.','H','E','A','D','E','R'}; + private final static char[] TEXT = {'.','T','E','X','T'}; + + private void parse() throws ParsingException { + if (!isNextNonSpace('(')) + throw new ParsingException( + "error in FETCH parsing, missing '(' at index " + index); + + List v = new ArrayList<>(); + Item i = null; + skipSpaces(); + do { + + if (index >= size) + throw new ParsingException( + "error in FETCH parsing, ran off end of buffer, size " + size); + + i = parseItem(); + if (i != null) + v.add(i); + else if (!parseExtensionItem()) + throw new ParsingException( + "error in FETCH parsing, unrecognized item at index " + + index + ", starts with \"" + next20() + "\""); + } while (!isNextNonSpace(')')); + + items = v.toArray(new Item[v.size()]); + } + + /** + * Return the next 20 characters in the buffer, for exception messages. + */ + private String next20() { + if (index + 20 > size) + return ASCIIUtility.toString(buffer, index, size); + else + return ASCIIUtility.toString(buffer, index, index + 20) + "..."; + } + + /** + * Parse the item at the current position in the buffer, + * skipping over the item if successful. Otherwise, return null + * and leave the buffer position unmodified. + */ + @SuppressWarnings("empty") + private Item parseItem() throws ParsingException { + switch (buffer[index]) { + case 'E': case 'e': + if (match(ENVELOPE.name)) + return new ENVELOPE(this); + break; + case 'F': case 'f': + if (match(FLAGS.name)) + return new FLAGS((IMAPResponse)this); + break; + case 'I': case 'i': + if (match(INTERNALDATE.name)) + return new INTERNALDATE(this); + break; + case 'B': case 'b': + if (match(BODYSTRUCTURE.name)) + return new BODYSTRUCTURE(this); + else if (match(BODY.name)) { + if (buffer[index] == '[') + return new BODY(this); + else + return new BODYSTRUCTURE(this); + } + break; + case 'R': case 'r': + if (match(RFC822SIZE.name)) + return new RFC822SIZE(this); + else if (match(RFC822DATA.name)) { + boolean isHeader = false; + if (match(HEADER)) + isHeader = true; // skip ".HEADER" + else if (match(TEXT)) + isHeader = false; // skip ".TEXT" + return new RFC822DATA(this, isHeader); + } + break; + case 'U': case 'u': + if (match(UID.name)) + return new UID(this); + break; + case 'M': case 'm': + if (match(MODSEQ.name)) + return new MODSEQ(this); + break; + default: + break; + } + return null; + } + + /** + * If this item is a known extension item, parse it. + */ + private boolean parseExtensionItem() throws ParsingException { + if (fitems == null) + return false; + for (int i = 0; i < fitems.length; i++) { + if (match(fitems[i].getName())) { + if (extensionItems == null) + extensionItems = new HashMap<>(); + extensionItems.put(fitems[i].getName(), + fitems[i].parseItem(this)); + return true; + } + } + return false; + } + + /** + * Does the current buffer match the given item name? + * itemName is the name of the IMAP item to compare against. + * NOTE that itemName *must* be all uppercase. + * If the match is successful, the buffer pointer (index) + * is incremented past the matched item. + */ + private boolean match(char[] itemName) { + int len = itemName.length; + for (int i = 0, j = index; i < len;) + // IMAP tokens are case-insensitive. We store itemNames in + // uppercase, so convert operand to uppercase before comparing. + if (Character.toUpperCase((char)buffer[j++]) != itemName[i++]) + return false; + index += len; + return true; + } + + /** + * Does the current buffer match the given item name? + * itemName is the name of the IMAP item to compare against. + * NOTE that itemName *must* be all uppercase. + * If the match is successful, the buffer pointer (index) + * is incremented past the matched item. + */ + private boolean match(String itemName) { + int len = itemName.length(); + for (int i = 0, j = index; i < len;) + // IMAP tokens are case-insensitive. We store itemNames in + // uppercase, so convert operand to uppercase before comparing. + if (Character.toUpperCase((char)buffer[j++]) != + itemName.charAt(i++)) + return false; + index += len; + return true; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/ID.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/ID.java new file mode 100644 index 000000000..e75b0a13e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/ID.java @@ -0,0 +1,121 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.*; +import com.sun.mail.iap.*; + +/** + * This class represents the response to the ID command.

    + * + * See RFC 2971. + * + * @since JavaMail 1.5.1 + * @author Bill Shannon + */ + +public class ID { + + private Map serverParams = null; + + /** + * Parse the server parameter list out of the response. + * + * @param r the response + * @exception ProtocolException for protocol failures + */ + public ID(Response r) throws ProtocolException { + // id_response ::= "ID" SPACE id_params_list + // id_params_list ::= "(" #(string SPACE nstring) ")" / nil + // ;; list of field value pairs + + r.skipSpaces(); + int c = r.peekByte(); + if (c == 'N' || c == 'n') // assume NIL + return; + + if (c != '(') + throw new ProtocolException("Missing '(' at start of ID"); + + serverParams = new HashMap<>(); + + String[] v = r.readStringList(); + if (v != null) { + for (int i = 0; i < v.length; i += 2) { + String name = v[i]; + if (name == null) + throw new ProtocolException("ID field name null"); + if (i + 1 >= v.length) + throw new ProtocolException("ID field without value: " + + name); + String value = v[i + 1]; + serverParams.put(name, value); + } + } + serverParams = Collections.unmodifiableMap(serverParams); + } + + /** + * Return the parsed server params. + */ + Map getServerParams() { + return serverParams; + } + + /** + * Convert the client parameters into an argument list for the ID command. + */ + static Argument getArgumentList(Map clientParams) { + Argument arg = new Argument(); + if (clientParams == null) { + arg.writeAtom("NIL"); + return arg; + } + Argument list = new Argument(); + // add params to list + for (Map.Entry e : clientParams.entrySet()) { + list.writeNString(e.getKey()); // assume these are ASCII only + list.writeNString(e.getValue()); + } + arg.writeArgument(list); + return arg; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPProtocol.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPProtocol.java new file mode 100644 index 000000000..aacb703e5 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPProtocol.java @@ -0,0 +1,3316 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.io.*; +import java.util.*; +import java.text.*; +import java.lang.reflect.*; +import java.util.logging.Level; +import java.nio.charset.StandardCharsets; + +import javax.mail.*; +import javax.mail.internet.*; +import javax.mail.search.*; + +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.MailLogger; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.util.BASE64EncoderStream; +import com.sun.mail.iap.*; +import com.sun.mail.auth.Ntlm; + +import com.sun.mail.imap.ACL; +import com.sun.mail.imap.Rights; +import com.sun.mail.imap.AppendUID; +import com.sun.mail.imap.CopyUID; +import com.sun.mail.imap.SortTerm; +import com.sun.mail.imap.ResyncData; +import com.sun.mail.imap.Utility; + +/** + * This class extends the iap.Protocol object and implements IMAP + * semantics. In general, there is a method corresponding to each + * IMAP protocol command. The typical implementation issues the + * appropriate protocol command, collects all responses, processes + * those responses that are specific to this command and then + * dispatches the rest (the unsolicited ones) to the dispatcher + * using the notifyResponseHandlers(r). + * + * @author John Mani + * @author Bill Shannon + */ + +public class IMAPProtocol extends Protocol { + + private boolean connected = false; // did constructor succeed? + private boolean rev1 = false; // REV1 server ? + private boolean referralException; // throw exception for IMAP REFERRAL? + private boolean noauthdebug = true; // hide auth info in debug output + private boolean authenticated; // authenticated? + // WARNING: authenticated may be set to true in superclass + // constructor, don't initialize it here. + + private Map capabilities; + // WARNING: capabilities may be initialized as a result of superclass + // constructor, don't initialize it here. + private List authmechs; + // WARNING: authmechs may be initialized as a result of superclass + // constructor, don't initialize it here. + private boolean utf8; // UTF-8 support enabled? + + protected SearchSequence searchSequence; + protected String[] searchCharsets; // array of search charsets + + protected Set enabled; // enabled capabilities - RFC 5161 + + private String name; + private SaslAuthenticator saslAuthenticator; // if SASL is being used + private String proxyAuthUser; // user name used with PROXYAUTH + + private ByteArray ba; // a buffer for fetchBody + + private static final byte[] CRLF = { (byte)'\r', (byte)'\n'}; + + private static final FetchItem[] fetchItems = { }; + + /** + * Constructor. + * Opens a connection to the given host at given port. + * + * @param name the protocol name + * @param host host to connect to + * @param port port number to connect to + * @param props Properties object used by this protocol + * @param isSSL true if SSL should be used + * @param logger the MailLogger to use for debug output + * @exception IOException for I/O errors + * @exception ProtocolException for protocol failures + */ + public IMAPProtocol(String name, String host, int port, + Properties props, boolean isSSL, MailLogger logger) + throws IOException, ProtocolException { + super(host, port, props, "mail." + name, isSSL, logger); + + try { + this.name = name; + noauthdebug = + !PropUtil.getBooleanProperty(props, "mail.debug.auth", false); + + // in case it was not initialized in processGreeting + referralException = PropUtil.getBooleanProperty(props, + prefix + ".referralexception", false); + + if (capabilities == null) + capability(); + + if (hasCapability("IMAP4rev1")) + rev1 = true; + + searchCharsets = new String[2]; // 2, for now. + searchCharsets[0] = "UTF-8"; + searchCharsets[1] = MimeUtility.mimeCharset( + MimeUtility.getDefaultJavaCharset() + ); + + connected = true; // must be last statement in constructor + } finally { + /* + * If we get here because an exception was thrown, we need + * to disconnect to avoid leaving a connected socket that + * no one will be able to use because this object was never + * completely constructed. + */ + if (!connected) + disconnect(); + } + } + + /** + * Constructor for debugging. + * + * @param in the InputStream from which to read + * @param out the PrintStream to which to write + * @param props Properties object used by this protocol + * @param debug true to enable debugging output + * @exception IOException for I/O errors + */ + public IMAPProtocol(InputStream in, PrintStream out, + Properties props, boolean debug) + throws IOException { + super(in, out, props, debug); + + this.name = "imap"; + noauthdebug = + !PropUtil.getBooleanProperty(props, "mail.debug.auth", false); + + if (capabilities == null) + capabilities = new HashMap<>(); + + searchCharsets = new String[2]; // 2, for now. + searchCharsets[0] = "UTF-8"; + searchCharsets[1] = MimeUtility.mimeCharset( + MimeUtility.getDefaultJavaCharset() + ); + + connected = true; // must be last statement in constructor + } + + /** + * Return an array of FetchItem objects describing the + * FETCH items supported by this protocol. Subclasses may + * override this method to combine their FetchItems with + * the FetchItems returned by the superclass. + * + * @return an array of FetchItem objects + * @since JavaMail 1.4.6 + */ + public FetchItem[] getFetchItems() { + return fetchItems; + } + + /** + * CAPABILITY command. + * + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.1.1" + */ + public void capability() throws ProtocolException { + // Check CAPABILITY + Response[] r = command("CAPABILITY", null); + Response response = r[r.length-1]; + + if (response.isOK()) + handleCapabilityResponse(r); + handleResult(response); + } + + /** + * Handle any untagged CAPABILITY response in the Response array. + * + * @param r the responses + */ + public void handleCapabilityResponse(Response[] r) { + boolean first = true; + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + + // Handle *all* untagged CAPABILITY responses. + // Though the spec seemingly states that only + // one CAPABILITY response string is allowed (6.1.1), + // some server vendors claim otherwise. + if (ir.keyEquals("CAPABILITY")) { + if (first) { + // clear out current when first response seen + capabilities = new HashMap<>(10); + authmechs = new ArrayList<>(5); + first = false; + } + parseCapabilities(ir); + } + } + } + + /** + * If the response contains a CAPABILITY response code, extract + * it and save the capabilities. + * + * @param r the response + */ + protected void setCapabilities(Response r) { + byte b; + while ((b = r.readByte()) > 0 && b != (byte)'[') + ; + if (b == 0) + return; + String s; + s = r.readAtom(); + if (!s.equalsIgnoreCase("CAPABILITY")) + return; + capabilities = new HashMap<>(10); + authmechs = new ArrayList<>(5); + parseCapabilities(r); + } + + /** + * Parse the capabilities from a CAPABILITY response or from + * a CAPABILITY response code attached to (e.g.) an OK response. + * + * @param r the CAPABILITY response + */ + protected void parseCapabilities(Response r) { + String s; + while ((s = r.readAtom()) != null) { + if (s.length() == 0) { + if (r.peekByte() == (byte)']') + break; + /* + * Probably found something here that's not an atom. + * Rather than loop forever or fail completely, we'll + * try to skip this bogus capability. This is known + * to happen with: + * Netscape Messaging Server 4.03 (built Apr 27 1999) + * that returns: + * * CAPABILITY * CAPABILITY IMAP4 IMAP4rev1 ... + * The "*" in the middle of the capability list causes + * us to loop forever here. + */ + r.skipToken(); + } else { + capabilities.put(s.toUpperCase(Locale.ENGLISH), s); + if (s.regionMatches(true, 0, "AUTH=", 0, 5)) { + authmechs.add(s.substring(5)); + if (logger.isLoggable(Level.FINE)) + logger.fine("AUTH: " + s.substring(5)); + } + } + } + } + + /** + * Check the greeting when first connecting; look for PREAUTH response. + * + * @param r the greeting response + * @exception ProtocolException for protocol failures + */ + @Override + protected void processGreeting(Response r) throws ProtocolException { + if (r.isBYE()) { + checkReferral(r); // may throw exception + throw new ConnectionException(this, r); + } + if (r.isOK()) { // check if it's OK + // XXX - is a REFERRAL response code really allowed here? + // XXX - referralException hasn't been initialized in c'tor yet + referralException = PropUtil.getBooleanProperty(props, + prefix + ".referralexception", false); + if (referralException) + checkReferral(r); + setCapabilities(r); + return; + } + // only other choice is PREAUTH + assert r instanceof IMAPResponse; + IMAPResponse ir = (IMAPResponse)r; + if (ir.keyEquals("PREAUTH")) { + authenticated = true; + setCapabilities(r); + } else { + disconnect(); + throw new ConnectionException(this, r); + } + } + + /** + * Check for an IMAP login REFERRAL response code. + * + * @exception IMAPReferralException if REFERRAL response code found + * @see "RFC 2221" + */ + private void checkReferral(Response r) throws IMAPReferralException { + String s = r.getRest(); // get the text after the response + if (s.startsWith("[")) { // a response code + int i = s.indexOf(' '); + if (i > 0 && s.substring(1, i).equalsIgnoreCase("REFERRAL")) { + String url, msg; + int j = s.indexOf(']'); + if (j > 0) { // should always be true; + url = s.substring(i + 1, j); + msg = s.substring(j + 1).trim(); + } else { + url = s.substring(i + 1); + msg = ""; + } + if (r.isBYE()) + disconnect(); + throw new IMAPReferralException(msg, url); + } + } + } + + /** + * Returns true if the connection has been authenticated, + * either due to a successful login, or due to a PREAUTH greeting response. + * + * @return true if the connection has been authenticated + */ + public boolean isAuthenticated() { + return authenticated; + } + + /** + * Returns true if this is an IMAP4rev1 server + * + * @return true if this is an IMAP4rev1 server + */ + public boolean isREV1() { + return rev1; + } + + /** + * Returns whether this Protocol supports non-synchronizing literals. + * + * @return true if non-synchronizing literals are supported + */ + @Override + protected boolean supportsNonSyncLiterals() { + return hasCapability("LITERAL+"); + } + + /** + * Read a response from the server. + * + * @return the response + * @exception IOException for I/O errors + * @exception ProtocolException for protocol failures + */ + @Override + public Response readResponse() throws IOException, ProtocolException { + // assert Thread.holdsLock(this); + // can't assert because it's called from constructor + IMAPResponse r = new IMAPResponse(this); + if (r.keyEquals("FETCH")) + r = new FetchResponse(r, getFetchItems()); + return r; + } + + /** + * Check whether the given capability is supported by + * this server. Returns true if so, otherwise + * returns false. + * + * @param c the capability name + * @return true if the server has the capability + */ + public boolean hasCapability(String c) { + if (c.endsWith("*")) { + c = c.substring(0, c.length() - 1).toUpperCase(Locale.ENGLISH); + Iterator it = capabilities.keySet().iterator(); + while (it.hasNext()) { + if (it.next().startsWith(c)) + return true; + } + return false; + } + return capabilities.containsKey(c.toUpperCase(Locale.ENGLISH)); + } + + /** + * Return the map of capabilities returned by the server. + * + * @return the Map of capabilities + * @since JavaMail 1.4.1 + */ + public Map getCapabilities() { + return capabilities; + } + + /** + * Does the server support UTF-8? + * + * @since JavaMail 1.6.0 + */ + public boolean supportsUtf8() { + return utf8; + } + + /** + * Close socket connection. + * + * This method just makes the Protocol.disconnect() method + * public. + */ + @Override + public void disconnect() { + super.disconnect(); + authenticated = false; // just in case + } + + /** + * The NOOP command. + * + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.1.2" + */ + public void noop() throws ProtocolException { + logger.fine("IMAPProtocol noop"); + simpleCommand("NOOP", null); + } + + /** + * LOGOUT Command. + * + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.1.3" + */ + public void logout() throws ProtocolException { + try { + Response[] r = command("LOGOUT", null); + + authenticated = false; + // dispatch any unsolicited responses. + // NOTE that the BYE response is dispatched here as well + notifyResponseHandlers(r); + } finally { + disconnect(); + } + } + + /** + * LOGIN Command. + * + * @param u the username + * @param p the password + * @throws ProtocolException as thrown by {@link Protocol#handleResult}. + * @see "RFC2060, section 6.2.2" + */ + public void login(String u, String p) throws ProtocolException { + Argument args = new Argument(); + args.writeString(u); + args.writeString(p); + + Response[] r = null; + try { + if (noauthdebug && isTracing()) { + logger.fine("LOGIN command trace suppressed"); + suspendTracing(); + } + r = command("LOGIN", args); + } finally { + resumeTracing(); + } + + // handle an illegal but not uncommon untagged CAPABILTY response + handleCapabilityResponse(r); + + // dispatch untagged responses + notifyResponseHandlers(r); + + // Handle result of this command + if (noauthdebug && isTracing()) + logger.fine("LOGIN command result: " + r[r.length-1]); + handleLoginResult(r[r.length-1]); + // If the response includes a CAPABILITY response code, process it + setCapabilities(r[r.length-1]); + // if we get this far without an exception, we're authenticated + authenticated = true; + } + + /** + * The AUTHENTICATE command with AUTH=LOGIN authenticate scheme + * + * @param u the username + * @param p the password + * @throws ProtocolException as thrown by {@link Protocol#handleResult}. + * @see "RFC2060, section 6.2.1" + */ + public synchronized void authlogin(String u, String p) + throws ProtocolException { + List v = new ArrayList<>(); + String tag = null; + Response r = null; + boolean done = false; + + try { + + if (noauthdebug && isTracing()) { + logger.fine("AUTHENTICATE LOGIN command trace suppressed"); + suspendTracing(); + } + + try { + tag = writeCommand("AUTHENTICATE LOGIN", null); + } catch (Exception ex) { + // Convert this into a BYE response + r = Response.byeResponse(ex); + done = true; + } + + OutputStream os = getOutputStream(); // stream to IMAP server + + /* Wrap a BASE64Encoder around a ByteArrayOutputstream + * to craft b64 encoded username and password strings + * + * Note that the encoded bytes should be sent "as-is" to the + * server, *not* as literals or quoted-strings. + * + * Also note that unlike the B64 definition in MIME, CRLFs + * should *not* be inserted during the encoding process. So, I + * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine, + * which should be sufficiently large ! + * + * Finally, format the line in a buffer so it can be sent as + * a single packet, to avoid triggering a bug in SUN's SIMS 2.0 + * server caused by patch 105346. + */ + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE); + boolean first = true; + + while (!done) { // loop till we are done + try { + r = readResponse(); + if (r.isContinuation()) { + // Server challenge .. + String s; + if (first) { // Send encoded username + s = u; + first = false; + } else // Send encoded password + s = p; + + // obtain b64 encoded bytes + b64os.write(s.getBytes(StandardCharsets.UTF_8)); + b64os.flush(); // complete the encoding + + bos.write(CRLF); // CRLF termination + os.write(bos.toByteArray()); // write out line + os.flush(); // flush the stream + bos.reset(); // reset buffer + } else if (r.isTagged() && r.getTag().equals(tag)) + // Ah, our tagged response + done = true; + else if (r.isBYE()) // outta here + done = true; + // hmm .. unsolicited response here ?! + } catch (Exception ioex) { + // convert this into a BYE response + r = Response.byeResponse(ioex); + done = true; + } + v.add(r); + } + + } finally { + resumeTracing(); + } + + Response[] responses = v.toArray(new Response[v.size()]); + + // handle an illegal but not uncommon untagged CAPABILTY response + handleCapabilityResponse(responses); + + /* + * Dispatch untagged responses. + * NOTE: in our current upper level IMAP classes, we add the + * responseHandler to the Protocol object only *after* the + * connection has been authenticated. So, for now, the below + * code really ends up being just a no-op. + */ + notifyResponseHandlers(responses); + + // Handle the final OK, NO, BAD or BYE response + if (noauthdebug && isTracing()) + logger.fine("AUTHENTICATE LOGIN command result: " + r); + handleLoginResult(r); + // If the response includes a CAPABILITY response code, process it + setCapabilities(r); + // if we get this far without an exception, we're authenticated + authenticated = true; + } + + + /** + * The AUTHENTICATE command with AUTH=PLAIN authentication scheme. + * This is based heavly on the {@link #authlogin} method. + * + * @param authzid the authorization id + * @param u the username + * @param p the password + * @throws ProtocolException as thrown by {@link Protocol#handleResult}. + * @see "RFC3501, section 6.2.2" + * @see "RFC2595, section 6" + * @since JavaMail 1.3.2 + */ + public synchronized void authplain(String authzid, String u, String p) + throws ProtocolException { + List v = new ArrayList<>(); + String tag = null; + Response r = null; + boolean done = false; + + try { + + if (noauthdebug && isTracing()) { + logger.fine("AUTHENTICATE PLAIN command trace suppressed"); + suspendTracing(); + } + + try { + tag = writeCommand("AUTHENTICATE PLAIN", null); + } catch (Exception ex) { + // Convert this into a BYE response + r = Response.byeResponse(ex); + done = true; + } + + OutputStream os = getOutputStream(); // stream to IMAP server + + /* Wrap a BASE64Encoder around a ByteArrayOutputstream + * to craft b64 encoded username and password strings + * + * Note that the encoded bytes should be sent "as-is" to the + * server, *not* as literals or quoted-strings. + * + * Also note that unlike the B64 definition in MIME, CRLFs + * should *not* be inserted during the encoding process. So, I + * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine, + * which should be sufficiently large ! + * + * Finally, format the line in a buffer so it can be sent as + * a single packet, to avoid triggering a bug in SUN's SIMS 2.0 + * server caused by patch 105346. + */ + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE); + + while (!done) { // loop till we are done + try { + r = readResponse(); + if (r.isContinuation()) { + // Server challenge .. + final String nullByte = "\0"; + String s = (authzid == null ? "" : authzid) + + nullByte + u + nullByte + p; + + // obtain b64 encoded bytes + b64os.write(s.getBytes(StandardCharsets.UTF_8)); + b64os.flush(); // complete the encoding + + bos.write(CRLF); // CRLF termination + os.write(bos.toByteArray()); // write out line + os.flush(); // flush the stream + bos.reset(); // reset buffer + } else if (r.isTagged() && r.getTag().equals(tag)) + // Ah, our tagged response + done = true; + else if (r.isBYE()) // outta here + done = true; + // hmm .. unsolicited response here ?! + } catch (Exception ioex) { + // convert this into a BYE response + r = Response.byeResponse(ioex); + done = true; + } + v.add(r); + } + + } finally { + resumeTracing(); + } + + Response[] responses = v.toArray(new Response[v.size()]); + + // handle an illegal but not uncommon untagged CAPABILTY response + handleCapabilityResponse(responses); + + /* + * Dispatch untagged responses. + * NOTE: in our current upper level IMAP classes, we add the + * responseHandler to the Protocol object only *after* the + * connection has been authenticated. So, for now, the below + * code really ends up being just a no-op. + */ + notifyResponseHandlers(responses); + + // Handle the final OK, NO, BAD or BYE response + if (noauthdebug && isTracing()) + logger.fine("AUTHENTICATE PLAIN command result: " + r); + handleLoginResult(r); + // If the response includes a CAPABILITY response code, process it + setCapabilities(r); + // if we get this far without an exception, we're authenticated + authenticated = true; + } + + /** + * The AUTHENTICATE command with AUTH=NTLM authentication scheme. + * This is based heavly on the {@link #authlogin} method. + * + * @param authzid the authorization id + * @param u the username + * @param p the password + * @throws ProtocolException as thrown by {@link Protocol#handleResult}. + * @see "RFC3501, section 6.2.2" + * @see "RFC2595, section 6" + * @since JavaMail 1.4.3 + */ + public synchronized void authntlm(String authzid, String u, String p) + throws ProtocolException { + List v = new ArrayList<>(); + String tag = null; + Response r = null; + boolean done = false; + + String type1Msg = null; + int flags = PropUtil.getIntProperty(props, + "mail." + name + ".auth.ntlm.flags", 0); + String domain = props.getProperty( + "mail." + name + ".auth.ntlm.domain", ""); + Ntlm ntlm = new Ntlm(domain, getLocalHost(), u, p, logger); + + try { + + if (noauthdebug && isTracing()) { + logger.fine("AUTHENTICATE NTLM command trace suppressed"); + suspendTracing(); + } + + try { + tag = writeCommand("AUTHENTICATE NTLM", null); + } catch (Exception ex) { + // Convert this into a BYE response + r = Response.byeResponse(ex); + done = true; + } + + OutputStream os = getOutputStream(); // stream to IMAP server + boolean first = true; + + while (!done) { // loop till we are done + try { + r = readResponse(); + if (r.isContinuation()) { + // Server challenge .. + String s; + if (first) { + s = ntlm.generateType1Msg(flags); + first = false; + } else { + s = ntlm.generateType3Msg(r.getRest()); + } + + os.write(s.getBytes(StandardCharsets.UTF_8)); + os.write(CRLF); // CRLF termination + os.flush(); // flush the stream + } else if (r.isTagged() && r.getTag().equals(tag)) + // Ah, our tagged response + done = true; + else if (r.isBYE()) // outta here + done = true; + // hmm .. unsolicited response here ?! + } catch (Exception ioex) { + // convert this into a BYE response + r = Response.byeResponse(ioex); + done = true; + } + v.add(r); + } + + } finally { + resumeTracing(); + } + + Response[] responses = v.toArray(new Response[v.size()]); + + // handle an illegal but not uncommon untagged CAPABILTY response + handleCapabilityResponse(responses); + + /* + * Dispatch untagged responses. + * NOTE: in our current upper level IMAP classes, we add the + * responseHandler to the Protocol object only *after* the + * connection has been authenticated. So, for now, the below + * code really ends up being just a no-op. + */ + notifyResponseHandlers(responses); + + // Handle the final OK, NO, BAD or BYE response + if (noauthdebug && isTracing()) + logger.fine("AUTHENTICATE NTLM command result: " + r); + handleLoginResult(r); + // If the response includes a CAPABILITY response code, process it + setCapabilities(r); + // if we get this far without an exception, we're authenticated + authenticated = true; + } + + /** + * The AUTHENTICATE command with AUTH=XOAUTH2 authentication scheme. + * This is based heavly on the {@link #authlogin} method. + * + * @param u the username + * @param p the password + * @throws ProtocolException as thrown by {@link Protocol#handleResult}. + * @see "RFC3501, section 6.2.2" + * @see "RFC2595, section 6" + * @since JavaMail 1.5.5 + */ + public synchronized void authoauth2(String u, String p) + throws ProtocolException { + List v = new ArrayList<>(); + String tag = null; + Response r = null; + boolean done = false; + + try { + + if (noauthdebug && isTracing()) { + logger.fine("AUTHENTICATE XOAUTH2 command trace suppressed"); + suspendTracing(); + } + + try { + Argument args = new Argument(); + args.writeAtom("XOAUTH2"); + if (hasCapability("SASL-IR")) { + String resp = "user=" + u + "\001auth=Bearer " + p + "\001\001"; + byte[] ba = BASE64EncoderStream.encode( + resp.getBytes(StandardCharsets.UTF_8)); + String irs = ASCIIUtility.toString(ba, 0, ba.length); + args.writeAtom(irs); + } + tag = writeCommand("AUTHENTICATE", args); + } catch (Exception ex) { + // Convert this into a BYE response + r = Response.byeResponse(ex); + done = true; + } + + OutputStream os = getOutputStream(); // stream to IMAP server + + while (!done) { // loop till we are done + try { + r = readResponse(); + if (r.isContinuation()) { + // Server challenge .. + String resp = "user=" + u + "\001auth=Bearer " + + p + "\001\001"; + byte[] b = BASE64EncoderStream.encode( + resp.getBytes(StandardCharsets.UTF_8)); + os.write(b); // write out response + os.write(CRLF); // CRLF termination + os.flush(); // flush the stream + } else if (r.isTagged() && r.getTag().equals(tag)) + // Ah, our tagged response + done = true; + else if (r.isBYE()) // outta here + done = true; + // hmm .. unsolicited response here ?! + } catch (Exception ioex) { + // convert this into a BYE response + r = Response.byeResponse(ioex); + done = true; + } + v.add(r); + } + + } finally { + resumeTracing(); + } + + Response[] responses = v.toArray(new Response[v.size()]); + + // handle an illegal but not uncommon untagged CAPABILTY response + handleCapabilityResponse(responses); + + /* + * Dispatch untagged responses. + * NOTE: in our current upper level IMAP classes, we add the + * responseHandler to the Protocol object only *after* the + * connection has been authenticated. So, for now, the below + * code really ends up being just a no-op. + */ + notifyResponseHandlers(responses); + + // Handle the final OK, NO, BAD or BYE response + if (noauthdebug && isTracing()) + logger.fine("AUTHENTICATE XOAUTH2 command result: " + r); + handleLoginResult(r); + // If the response includes a CAPABILITY response code, process it + setCapabilities(r); + // if we get this far without an exception, we're authenticated + authenticated = true; + } + + /** + * SASL-based login. + * + * @param allowed the SASL mechanisms we're allowed to use + * @param realm the SASL realm + * @param authzid the authorization id + * @param u the username + * @param p the password + * @exception ProtocolException for protocol failures + */ + public void sasllogin(String[] allowed, String realm, String authzid, + String u, String p) throws ProtocolException { + boolean useCanonicalHostName = PropUtil.getBooleanProperty(props, + "mail." + name + ".sasl.usecanonicalhostname", false); + String serviceHost; + if (useCanonicalHostName) + serviceHost = getInetAddress().getCanonicalHostName(); + else + serviceHost = host; + if (saslAuthenticator == null) { + try { + Class sac = Class.forName( + "com.sun.mail.imap.protocol.IMAPSaslAuthenticator"); + Constructor c = sac.getConstructor(new Class[] { + IMAPProtocol.class, + String.class, + Properties.class, + MailLogger.class, + String.class + }); + saslAuthenticator = (SaslAuthenticator)c.newInstance( + new Object[] { + this, + name, + props, + logger, + serviceHost + }); + } catch (Exception ex) { + logger.log(Level.FINE, "Can't load SASL authenticator", ex); + // probably because we're running on a system without SASL + return; // not authenticated, try without SASL + } + } + + // were any allowed mechanisms specified? + List v; + if (allowed != null && allowed.length > 0) { + // remove anything not supported by the server + v = new ArrayList<>(allowed.length); + for (int i = 0; i < allowed.length; i++) + if (authmechs.contains(allowed[i])) // XXX - case must match + v.add(allowed[i]); + } else { + // everything is allowed + v = authmechs; + } + String[] mechs = v.toArray(new String[v.size()]); + + try { + + if (noauthdebug && isTracing()) { + logger.fine("SASL authentication command trace suppressed"); + suspendTracing(); + } + + if (saslAuthenticator.authenticate(mechs, realm, authzid, u, p)) { + if (noauthdebug && isTracing()) + logger.fine("SASL authentication succeeded"); + authenticated = true; + } else { + if (noauthdebug && isTracing()) + logger.fine("SASL authentication failed"); + } + } finally { + resumeTracing(); + } + } + + // XXX - for IMAPSaslAuthenticator access to protected method + OutputStream getIMAPOutputStream() { + return getOutputStream(); + } + + /** + * Handle the result response for a LOGIN or AUTHENTICATE command. + * Look for IMAP login REFERRAL. + * + * @param r the response + * @exception ProtocolException for protocol failures + * @since JavaMail 1.5.5 + */ + protected void handleLoginResult(Response r) throws ProtocolException { + if (hasCapability("LOGIN-REFERRALS") && + (!r.isOK() || referralException)) + checkReferral(r); + handleResult(r); + } + + /** + * PROXYAUTH Command. + * + * @param u the PROXYAUTH user name + * @exception ProtocolException for protocol failures + * @see "Netscape/iPlanet/SunONE Messaging Server extension" + */ + public void proxyauth(String u) throws ProtocolException { + Argument args = new Argument(); + args.writeString(u); + + simpleCommand("PROXYAUTH", args); + proxyAuthUser = u; + } + + /** + * Get the user name used with the PROXYAUTH command. + * Returns null if PROXYAUTH was not used. + * + * @return the PROXYAUTH user name + * @since JavaMail 1.5.1 + */ + public String getProxyAuthUser() { + return proxyAuthUser; + } + + /** + * UNAUTHENTICATE Command. + * + * @exception ProtocolException for protocol failures + * @see "Netscape/iPlanet/SunONE Messaging Server extension" + * @since JavaMail 1.5.1 + */ + public void unauthenticate() throws ProtocolException { + if (!hasCapability("X-UNAUTHENTICATE")) + throw new BadCommandException("UNAUTHENTICATE not supported"); + simpleCommand("UNAUTHENTICATE", null); + authenticated = false; + } + + /** + * ID Command, for Yahoo! Mail IMAP server. + * + * @param guid the GUID + * @exception ProtocolException for protocol failures + * @deprecated As of JavaMail 1.5.1, replaced by + * {@link #id(Map) id(Map<String,String>)} + * @since JavaMail 1.4.4 + */ + @Deprecated + public void id(String guid) throws ProtocolException { + // support this for now, but remove it soon + Map gmap = new HashMap<>(); + gmap.put("GUID", guid); + id(gmap); + } + + /** + * STARTTLS Command. + * + * @exception ProtocolException for protocol failures + * @see "RFC3501, section 6.2.1" + */ + public void startTLS() throws ProtocolException { + try { + super.startTLS("STARTTLS"); + } catch (ProtocolException pex) { + logger.log(Level.FINE, "STARTTLS ProtocolException", pex); + // ProtocolException just means the command wasn't recognized, + // or failed. This should never happen if we check the + // CAPABILITY first. + throw pex; + } catch (Exception ex) { + logger.log(Level.FINE, "STARTTLS Exception", ex); + // any other exception means we have to shut down the connection + // generate an artificial BYE response and disconnect + Response[] r = { Response.byeResponse(ex) }; + notifyResponseHandlers(r); + disconnect(); + throw new ProtocolException("STARTTLS failure", ex); + } + } + + /** + * COMPRESS Command. Only supports DEFLATE. + * + * @exception ProtocolException for protocol failures + * @see "RFC 4978" + */ + public void compress() throws ProtocolException { + try { + super.startCompression("COMPRESS DEFLATE"); + } catch (ProtocolException pex) { + logger.log(Level.FINE, "COMPRESS ProtocolException", pex); + // ProtocolException just means the command wasn't recognized, + // or failed. This should never happen if we check the + // CAPABILITY first. + throw pex; + } catch (Exception ex) { + logger.log(Level.FINE, "COMPRESS Exception", ex); + // any other exception means we have to shut down the connection + // generate an artificial BYE response and disconnect + Response[] r = { Response.byeResponse(ex) }; + notifyResponseHandlers(r); + disconnect(); + throw new ProtocolException("COMPRESS failure", ex); + } + } + + /** + * Encode a mailbox name appropriately depending on whether or not + * the server supports UTF-8, and add the encoded name to the + * Argument. + * + * @param args the arguments + * @param name the name to encode + * @since JavaMail 1.6.0 + */ + protected void writeMailboxName(Argument args, String name) { + if (utf8) + args.writeString(name, StandardCharsets.UTF_8); + else + // encode the mbox as per RFC2060 + args.writeString(BASE64MailboxEncoder.encode(name)); + } + + /** + * SELECT Command. + * + * @param mbox the mailbox name + * @return MailboxInfo if successful + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.1" + */ + public MailboxInfo select(String mbox) throws ProtocolException { + return select(mbox, null); + } + + /** + * SELECT Command with QRESYNC data. + * + * @param mbox the mailbox name + * @param rd the ResyncData + * @return MailboxInfo if successful + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.1" + * @see "RFC5162, section 3.1" + * @since JavaMail 1.5.1 + */ + public MailboxInfo select(String mbox, ResyncData rd) + throws ProtocolException { + Argument args = new Argument(); + writeMailboxName(args, mbox); + + if (rd != null) { + if (rd == ResyncData.CONDSTORE) { + if (!hasCapability("CONDSTORE")) + throw new BadCommandException("CONDSTORE not supported"); + args.writeArgument(new Argument().writeAtom("CONDSTORE")); + } else { + if (!hasCapability("QRESYNC")) + throw new BadCommandException("QRESYNC not supported"); + args.writeArgument(resyncArgs(rd)); + } + } + + Response[] r = command("SELECT", args); + + // Note that MailboxInfo also removes those responses + // it knows about + MailboxInfo minfo = new MailboxInfo(r); + + // dispatch any remaining untagged responses + notifyResponseHandlers(r); + + Response response = r[r.length-1]; + + if (response.isOK()) { // command succesful + if (response.toString().indexOf("READ-ONLY") != -1) + minfo.mode = Folder.READ_ONLY; + else + minfo.mode = Folder.READ_WRITE; + } + + handleResult(response); + return minfo; + } + + /** + * EXAMINE Command. + * + * @param mbox the mailbox name + * @return MailboxInfo if successful + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.2" + */ + public MailboxInfo examine(String mbox) throws ProtocolException { + return examine(mbox, null); + } + + /** + * EXAMINE Command with QRESYNC data. + * + * @param mbox the mailbox name + * @param rd the ResyncData + * @return MailboxInfo if successful + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.2" + * @see "RFC5162, section 3.1" + * @since JavaMail 1.5.1 + */ + public MailboxInfo examine(String mbox, ResyncData rd) + throws ProtocolException { + Argument args = new Argument(); + writeMailboxName(args, mbox); + + if (rd != null) { + if (rd == ResyncData.CONDSTORE) { + if (!hasCapability("CONDSTORE")) + throw new BadCommandException("CONDSTORE not supported"); + args.writeArgument(new Argument().writeAtom("CONDSTORE")); + } else { + if (!hasCapability("QRESYNC")) + throw new BadCommandException("QRESYNC not supported"); + args.writeArgument(resyncArgs(rd)); + } + } + + Response[] r = command("EXAMINE", args); + + // Note that MailboxInfo also removes those responses + // it knows about + MailboxInfo minfo = new MailboxInfo(r); + minfo.mode = Folder.READ_ONLY; // Obviously + + // dispatch any remaining untagged responses + notifyResponseHandlers(r); + + handleResult(r[r.length-1]); + return minfo; + } + + /** + * Generate a QRESYNC argument list based on the ResyncData. + */ + private static Argument resyncArgs(ResyncData rd) { + Argument cmd = new Argument(); + cmd.writeAtom("QRESYNC"); + Argument args = new Argument(); + args.writeNumber(rd.getUIDValidity()); + args.writeNumber(rd.getModSeq()); + UIDSet[] uids = Utility.getResyncUIDSet(rd); + if (uids != null) + args.writeString(UIDSet.toString(uids)); + cmd.writeArgument(args); + return cmd; + } + + /** + * ENABLE Command. + * + * @param cap the name of the capability to enable + * @exception ProtocolException for protocol failures + * @see "RFC 5161" + * @since JavaMail 1.5.1 + */ + public void enable(String cap) throws ProtocolException { + if (!hasCapability("ENABLE")) + throw new BadCommandException("ENABLE not supported"); + Argument args = new Argument(); + args.writeAtom(cap); + simpleCommand("ENABLE", args); + if (enabled == null) + enabled = new HashSet<>(); + enabled.add(cap.toUpperCase(Locale.ENGLISH)); + + // update the utf8 flag + utf8 = isEnabled("UTF8=ACCEPT"); + } + + /** + * Is the capability/extension enabled? + * + * @param cap the capability name + * @return true if enabled + * @see "RFC 5161" + * @since JavaMail 1.5.1 + */ + public boolean isEnabled(String cap) { + if (enabled == null) + return false; + else + return enabled.contains(cap.toUpperCase(Locale.ENGLISH)); + } + + /** + * UNSELECT Command. + * + * @exception ProtocolException for protocol failures + * @see "RFC 3691" + * @since JavaMail 1.4.4 + */ + public void unselect() throws ProtocolException { + if (!hasCapability("UNSELECT")) + throw new BadCommandException("UNSELECT not supported"); + simpleCommand("UNSELECT", null); + } + + /** + * STATUS Command. + * + * @param mbox the mailbox + * @param items the STATUS items to request + * @return STATUS results + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.10" + */ + public Status status(String mbox, String[] items) + throws ProtocolException { + if (!isREV1() && !hasCapability("IMAP4SUNVERSION")) + // STATUS is rev1 only, however the non-rev1 SIMS2.0 + // does support this. + throw new BadCommandException("STATUS not supported"); + + Argument args = new Argument(); + writeMailboxName(args, mbox); + + Argument itemArgs = new Argument(); + if (items == null) + items = Status.standardItems; + + for (int i = 0, len = items.length; i < len; i++) + itemArgs.writeAtom(items[i]); + args.writeArgument(itemArgs); + + Response[] r = command("STATUS", args); + + Status status = null; + Response response = r[r.length-1]; + + // Grab all STATUS responses + if (response.isOK()) { // command succesful + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals("STATUS")) { + if (status == null) + status = new Status(ir); + else // collect 'em all + Status.add(status, new Status(ir)); + r[i] = null; + } + } + } + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + return status; + } + + /** + * CREATE Command. + * + * @param mbox the mailbox to create + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.3" + */ + public void create(String mbox) throws ProtocolException { + Argument args = new Argument(); + writeMailboxName(args, mbox); + + simpleCommand("CREATE", args); + } + + /** + * DELETE Command. + * + * @param mbox the mailbox to delete + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.4" + */ + public void delete(String mbox) throws ProtocolException { + Argument args = new Argument(); + writeMailboxName(args, mbox); + + simpleCommand("DELETE", args); + } + + /** + * RENAME Command. + * + * @param o old mailbox name + * @param n new mailbox name + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.5" + */ + public void rename(String o, String n) throws ProtocolException { + Argument args = new Argument(); + writeMailboxName(args, o); + writeMailboxName(args, n); + + simpleCommand("RENAME", args); + } + + /** + * SUBSCRIBE Command. + * + * @param mbox the mailbox + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.6" + */ + public void subscribe(String mbox) throws ProtocolException { + Argument args = new Argument(); + writeMailboxName(args, mbox); + + simpleCommand("SUBSCRIBE", args); + } + + /** + * UNSUBSCRIBE Command. + * + * @param mbox the mailbox + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.7" + */ + public void unsubscribe(String mbox) throws ProtocolException { + Argument args = new Argument(); + writeMailboxName(args, mbox); + + simpleCommand("UNSUBSCRIBE", args); + } + + /** + * LIST Command. + * + * @param ref reference string + * @param pattern pattern to list + * @return LIST results + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.8" + */ + public ListInfo[] list(String ref, String pattern) + throws ProtocolException { + return doList("LIST", ref, pattern); + } + + /** + * LSUB Command. + * + * @param ref reference string + * @param pattern pattern to list + * @return LSUB results + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.9" + */ + public ListInfo[] lsub(String ref, String pattern) + throws ProtocolException { + return doList("LSUB", ref, pattern); + } + + /** + * Execute the specified LIST-like command (e.g., "LIST" or "LSUB"), + * using the reference and pattern. + * + * @param cmd the list command + * @param ref the reference string + * @param pat the pattern + * @return array of ListInfo results + * @exception ProtocolException for protocol failures + * @since JavaMail 1.4.6 + */ + protected ListInfo[] doList(String cmd, String ref, String pat) + throws ProtocolException { + Argument args = new Argument(); + writeMailboxName(args, ref); + writeMailboxName(args, pat); + + Response[] r = command(cmd, args); + + ListInfo[] linfo = null; + Response response = r[r.length-1]; + + if (response.isOK()) { // command succesful + List v = new ArrayList<>(1); + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals(cmd)) { + v.add(new ListInfo(ir)); + r[i] = null; + } + } + if (v.size() > 0) { + linfo = v.toArray(new ListInfo[v.size()]); + } + } + + // Dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + return linfo; + } + + /** + * APPEND Command. + * + * @param mbox the mailbox + * @param f the message Flags + * @param d the message date + * @param data the message data + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.11" + */ + public void append(String mbox, Flags f, Date d, + Literal data) throws ProtocolException { + appenduid(mbox, f, d, data, false); // ignore return value + } + + /** + * APPEND Command, return uid from APPENDUID response code. + * + * @param mbox the mailbox + * @param f the message Flags + * @param d the message date + * @param data the message data + * @return APPENDUID data + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.3.11" + */ + public AppendUID appenduid(String mbox, Flags f, Date d, + Literal data) throws ProtocolException { + return appenduid(mbox, f, d, data, true); + } + + public AppendUID appenduid(String mbox, Flags f, Date d, + Literal data, boolean uid) throws ProtocolException { + Argument args = new Argument(); + writeMailboxName(args, mbox); + + if (f != null) { // set Flags in appended message + // can't set the \Recent flag in APPEND + if (f.contains(Flags.Flag.RECENT)) { + f = new Flags(f); // copy, don't modify orig + f.remove(Flags.Flag.RECENT); // remove RECENT from copy + } + + /* + * HACK ALERT: We want the flag_list to be written out + * without any checking/processing of the bytes in it. If + * I use writeString(), the flag_list will end up being + * quoted since it contains "illegal" characters. So I + * am depending on implementation knowledge that writeAtom() + * does not do any checking/processing - it just writes out + * the bytes. What we really need is a writeFoo() that just + * dumps out its argument. + */ + args.writeAtom(createFlagList(f)); + } + if (d != null) // set INTERNALDATE in appended message + args.writeString(INTERNALDATE.format(d)); + + args.writeBytes(data); + + Response[] r = command("APPEND", args); + + // dispatch untagged responses + notifyResponseHandlers(r); + + // Handle result of this command + handleResult(r[r.length-1]); + + if (uid) + return getAppendUID(r[r.length-1]); + else + return null; + } + + /** + * If the response contains an APPENDUID response code, extract + * it and return an AppendUID object with the information. + */ + private AppendUID getAppendUID(Response r) { + if (!r.isOK()) + return null; + byte b; + while ((b = r.readByte()) > 0 && b != (byte)'[') + ; + if (b == 0) + return null; + String s; + s = r.readAtom(); + if (!s.equalsIgnoreCase("APPENDUID")) + return null; + + long uidvalidity = r.readLong(); + long uid = r.readLong(); + return new AppendUID(uidvalidity, uid); + } + + /** + * CHECK Command. + * + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.4.1" + */ + public void check() throws ProtocolException { + simpleCommand("CHECK", null); + } + + /** + * CLOSE Command. + * + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.4.2" + */ + public void close() throws ProtocolException { + simpleCommand("CLOSE", null); + } + + /** + * EXPUNGE Command. + * + * @exception ProtocolException for protocol failures + * @see "RFC2060, section 6.4.3" + */ + public void expunge() throws ProtocolException { + simpleCommand("EXPUNGE", null); + } + + /** + * UID EXPUNGE Command. + * + * @param set UIDs to expunge + * @exception ProtocolException for protocol failures + * @see "RFC4315, section 2" + */ + public void uidexpunge(UIDSet[] set) throws ProtocolException { + if (!hasCapability("UIDPLUS")) + throw new BadCommandException("UID EXPUNGE not supported"); + simpleCommand("UID EXPUNGE " + UIDSet.toString(set), null); + } + + /** + * Fetch the BODYSTRUCTURE of the specified message. + * + * @param msgno the message number + * @return the BODYSTRUCTURE item + * @exception ProtocolException for protocol failures + */ + public BODYSTRUCTURE fetchBodyStructure(int msgno) + throws ProtocolException { + Response[] r = fetch(msgno, "BODYSTRUCTURE"); + notifyResponseHandlers(r); + + Response response = r[r.length-1]; + if (response.isOK()) + return FetchResponse.getItem(r, msgno, BODYSTRUCTURE.class); + else if (response.isNO()) + return null; + else { + handleResult(response); + return null; + } + } + + /** + * Fetch given BODY section, without marking the message + * as SEEN. + * + * @param msgno the message number + * @param section the body section + * @return the BODY item + * @exception ProtocolException for protocol failures + */ + public BODY peekBody(int msgno, String section) + throws ProtocolException { + return fetchBody(msgno, section, true); + } + + /** + * Fetch given BODY section. + * + * @param msgno the message number + * @param section the body section + * @return the BODY item + * @exception ProtocolException for protocol failures + */ + public BODY fetchBody(int msgno, String section) + throws ProtocolException { + return fetchBody(msgno, section, false); + } + + protected BODY fetchBody(int msgno, String section, boolean peek) + throws ProtocolException { + Response[] r; + + if (section == null) + section = ""; + String body = (peek ? "BODY.PEEK[" : "BODY[") + section + "]"; + return fetchSectionBody(msgno, section, body); + } + + /** + * Partial FETCH of given BODY section, without setting SEEN flag. + * + * @param msgno the message number + * @param section the body section + * @param start starting byte count + * @param size number of bytes to fetch + * @return the BODY item + * @exception ProtocolException for protocol failures + */ + public BODY peekBody(int msgno, String section, int start, int size) + throws ProtocolException { + return fetchBody(msgno, section, start, size, true, null); + } + + /** + * Partial FETCH of given BODY section. + * + * @param msgno the message number + * @param section the body section + * @param start starting byte count + * @param size number of bytes to fetch + * @return the BODY item + * @exception ProtocolException for protocol failures + */ + public BODY fetchBody(int msgno, String section, int start, int size) + throws ProtocolException { + return fetchBody(msgno, section, start, size, false, null); + } + + /** + * Partial FETCH of given BODY section, without setting SEEN flag. + * + * @param msgno the message number + * @param section the body section + * @param start starting byte count + * @param size number of bytes to fetch + * @param ba the buffer into which to read the response + * @return the BODY item + * @exception ProtocolException for protocol failures + */ + public BODY peekBody(int msgno, String section, int start, int size, + ByteArray ba) throws ProtocolException { + return fetchBody(msgno, section, start, size, true, ba); + } + + /** + * Partial FETCH of given BODY section. + * + * @param msgno the message number + * @param section the body section + * @param start starting byte count + * @param size number of bytes to fetch + * @param ba the buffer into which to read the response + * @return the BODY item + * @exception ProtocolException for protocol failures + */ + public BODY fetchBody(int msgno, String section, int start, int size, + ByteArray ba) throws ProtocolException { + return fetchBody(msgno, section, start, size, false, ba); + } + + protected BODY fetchBody(int msgno, String section, int start, int size, + boolean peek, ByteArray ba) throws ProtocolException { + this.ba = ba; // save for later use by getResponseBuffer + if (section == null) + section = ""; + String body = (peek ? "BODY.PEEK[" : "BODY[") + section + "]<" + + String.valueOf(start) + "." + + String.valueOf(size) + ">"; + return fetchSectionBody(msgno, section, body); + } + + /** + * Fetch the given body section of the given message, using the + * body string "body". + * + * @param msgno the message number + * @param section the body section + * @param body the body string + * @return the BODY item + * @exception ProtocolException for protocol failures + */ + protected BODY fetchSectionBody(int msgno, String section, String body) + throws ProtocolException { + Response[] r; + + r = fetch(msgno, body); + notifyResponseHandlers(r); + + Response response = r[r.length-1]; + if (response.isOK()) { + List bl = FetchResponse.getItems(r, msgno, BODY.class); + if (bl.size() == 1) + return bl.get(0); // the common case + if (logger.isLoggable(Level.FINEST)) + logger.finest("got " + bl.size() + + " BODY responses for section " + section); + // more then one BODY response, have to find the right one + for (BODY br : bl) { + if (logger.isLoggable(Level.FINEST)) + logger.finest("got BODY section " + br.getSection()); + if (br.getSection().equalsIgnoreCase(section)) + return br; // that's the one! + } + return null; // couldn't find it + } else if (response.isNO()) + return null; + else { + handleResult(response); + return null; + } + } + + /** + * Return a buffer to read a response into. + * The buffer is provided by fetchBody and is + * used only once. + * + * @return the buffer to use + */ + @Override + protected ByteArray getResponseBuffer() { + ByteArray ret = ba; + ba = null; + return ret; + } + + /** + * Fetch the specified RFC822 Data item. 'what' names + * the item to be fetched. 'what' can be null + * to fetch the whole message. + * + * @param msgno the message number + * @param what the item to fetch + * @return the RFC822DATA item + * @exception ProtocolException for protocol failures + */ + public RFC822DATA fetchRFC822(int msgno, String what) + throws ProtocolException { + Response[] r = fetch(msgno, + what == null ? "RFC822" : "RFC822." + what + ); + + // dispatch untagged responses + notifyResponseHandlers(r); + + Response response = r[r.length-1]; + if (response.isOK()) + return FetchResponse.getItem(r, msgno, RFC822DATA.class); + else if (response.isNO()) + return null; + else { + handleResult(response); + return null; + } + } + + /** + * Fetch the FLAGS for the given message. + * + * @param msgno the message number + * @return the Flags + * @exception ProtocolException for protocol failures + */ + public Flags fetchFlags(int msgno) throws ProtocolException { + Flags flags = null; + Response[] r = fetch(msgno, "FLAGS"); + + // Search for our FLAGS response + for (int i = 0, len = r.length; i < len; i++) { + if (r[i] == null || + !(r[i] instanceof FetchResponse) || + ((FetchResponse)r[i]).getNumber() != msgno) + continue; + + FetchResponse fr = (FetchResponse)r[i]; + if ((flags = fr.getItem(FLAGS.class)) != null) { + r[i] = null; // remove this response + break; + } + } + + // dispatch untagged responses + notifyResponseHandlers(r); + handleResult(r[r.length-1]); + return flags; + } + + /** + * Fetch the IMAP UID for the given message. + * + * @param msgno the message number + * @return the UID + * @exception ProtocolException for protocol failures + */ + public UID fetchUID(int msgno) throws ProtocolException { + Response[] r = fetch(msgno, "UID"); + + // dispatch untagged responses + notifyResponseHandlers(r); + + Response response = r[r.length-1]; + if (response.isOK()) + return FetchResponse.getItem(r, msgno, UID.class); + else if (response.isNO()) // XXX: Issue NOOP ? + return null; + else { + handleResult(response); + return null; // NOTREACHED + } + } + + /** + * Fetch the IMAP MODSEQ for the given message. + * + * @param msgno the message number + * @return the MODSEQ + * @exception ProtocolException for protocol failures + * @since JavaMail 1.5.1 + */ + public MODSEQ fetchMODSEQ(int msgno) throws ProtocolException { + Response[] r = fetch(msgno, "MODSEQ"); + + // dispatch untagged responses + notifyResponseHandlers(r); + + Response response = r[r.length-1]; + if (response.isOK()) + return FetchResponse.getItem(r, msgno, MODSEQ.class); + else if (response.isNO()) // XXX: Issue NOOP ? + return null; + else { + handleResult(response); + return null; // NOTREACHED + } + } + + /** + * Get the sequence number for the given UID. Nothing is returned; + * the FETCH UID response must be handled by the reponse handler, + * along with any possible EXPUNGE responses, to ensure that the + * UID is matched with the correct sequence number. + * + * @param uid the UID + * @exception ProtocolException for protocol failures + * @since JavaMail 1.5.3 + */ + public void fetchSequenceNumber(long uid) throws ProtocolException { + Response[] r = fetch(String.valueOf(uid), "UID", true); + + notifyResponseHandlers(r); + handleResult(r[r.length-1]); + } + + /** + * Get the sequence numbers for UIDs ranging from start till end. + * Since the range may be large and sparse, an array of the UIDs actually + * found is returned. The caller must map these to messages after + * the FETCH UID responses have been handled by the reponse handler, + * along with any possible EXPUNGE responses, to ensure that the + * UIDs are matched with the correct sequence numbers. + * + * @param start first UID + * @param end last UID + * @return array of sequence numbers + * @exception ProtocolException for protocol failures + * @since JavaMail 1.5.3 + */ + public long[] fetchSequenceNumbers(long start, long end) + throws ProtocolException { + Response[] r = fetch(String.valueOf(start) + ":" + + (end == UIDFolder.LASTUID ? "*" : + String.valueOf(end)), + "UID", true); + + UID u; + List v = new ArrayList<>(); + for (int i = 0, len = r.length; i < len; i++) { + if (r[i] == null || !(r[i] instanceof FetchResponse)) + continue; + + FetchResponse fr = (FetchResponse)r[i]; + if ((u = fr.getItem(UID.class)) != null) + v.add(u); + } + + notifyResponseHandlers(r); + handleResult(r[r.length-1]); + + long[] lv = new long[v.size()]; + for (int i = 0; i < v.size(); i++) + lv[i] = v.get(i).uid; + return lv; + } + + /** + * Get the sequence numbers for UIDs specified in the array. + * Nothing is returned. The caller must map the UIDs to messages after + * the FETCH UID responses have been handled by the reponse handler, + * along with any possible EXPUNGE responses, to ensure that the + * UIDs are matched with the correct sequence numbers. + * + * @param uids the UIDs + * @exception ProtocolException for protocol failures + * @since JavaMail 1.5.3 + */ + public void fetchSequenceNumbers(long[] uids) throws ProtocolException { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < uids.length; i++) { + if (i > 0) + sb.append(","); + sb.append(String.valueOf(uids[i])); + } + + Response[] r = fetch(sb.toString(), "UID", true); + + notifyResponseHandlers(r); + handleResult(r[r.length-1]); + } + + /** + * Get the sequence numbers for messages changed since the given + * modseq and with UIDs ranging from start till end. + * Also, prefetch the flags for the returned messages. + * + * @param start first UID + * @param end last UID + * @param modseq the MODSEQ + * @return array of sequence numbers + * @exception ProtocolException for protocol failures + * @see "RFC 4551" + * @since JavaMail 1.5.1 + */ + public int[] uidfetchChangedSince(long start, long end, long modseq) + throws ProtocolException { + String msgSequence = String.valueOf(start) + ":" + + (end == UIDFolder.LASTUID ? "*" : + String.valueOf(end)); + Response[] r = command("UID FETCH " + msgSequence + + " (FLAGS) (CHANGEDSINCE " + String.valueOf(modseq) + ")", null); + + List v = new ArrayList<>(); + for (int i = 0, len = r.length; i < len; i++) { + if (r[i] == null || !(r[i] instanceof FetchResponse)) + continue; + + FetchResponse fr = (FetchResponse)r[i]; + v.add(Integer.valueOf(fr.getNumber())); + } + + notifyResponseHandlers(r); + handleResult(r[r.length-1]); + + // Copy the list into 'matches' + int vsize = v.size(); + int[] matches = new int[vsize]; + for (int i = 0; i < vsize; i++) + matches[i] = v.get(i).intValue(); + return matches; + } + + public Response[] fetch(MessageSet[] msgsets, String what) + throws ProtocolException { + return fetch(MessageSet.toString(msgsets), what, false); + } + + public Response[] fetch(int start, int end, String what) + throws ProtocolException { + return fetch(String.valueOf(start) + ":" + String.valueOf(end), + what, false); + } + + public Response[] fetch(int msg, String what) + throws ProtocolException { + return fetch(String.valueOf(msg), what, false); + } + + private Response[] fetch(String msgSequence, String what, boolean uid) + throws ProtocolException { + if (uid) + return command("UID FETCH " + msgSequence +" (" + what + ")",null); + else + return command("FETCH " + msgSequence + " (" + what + ")", null); + } + + /** + * COPY command. + * + * @param msgsets the messages to copy + * @param mbox the mailbox to copy them to + * @exception ProtocolException for protocol failures + */ + public void copy(MessageSet[] msgsets, String mbox) + throws ProtocolException { + copyuid(MessageSet.toString(msgsets), mbox, false); + } + + /** + * COPY command. + * + * @param start start message number + * @param end end message number + * @param mbox the mailbox to copy them to + * @exception ProtocolException for protocol failures + */ + public void copy(int start, int end, String mbox) + throws ProtocolException { + copyuid(String.valueOf(start) + ":" + String.valueOf(end), + mbox, false); + } + + /** + * COPY command, return uid from COPYUID response code. + * + * @param msgsets the messages to copy + * @param mbox the mailbox to copy them to + * @return COPYUID response data + * @exception ProtocolException for protocol failures + * @see "RFC 4315, section 3" + */ + public CopyUID copyuid(MessageSet[] msgsets, String mbox) + throws ProtocolException { + return copyuid(MessageSet.toString(msgsets), mbox, true); + } + + /** + * COPY command, return uid from COPYUID response code. + * + * @param start start message number + * @param end end message number + * @param mbox the mailbox to copy them to + * @return COPYUID response data + * @exception ProtocolException for protocol failures + * @see "RFC 4315, section 3" + */ + public CopyUID copyuid(int start, int end, String mbox) + throws ProtocolException { + return copyuid(String.valueOf(start) + ":" + String.valueOf(end), + mbox, true); + } + + private CopyUID copyuid(String msgSequence, String mbox, boolean uid) + throws ProtocolException { + if (uid && !hasCapability("UIDPLUS")) + throw new BadCommandException("UIDPLUS not supported"); + + Argument args = new Argument(); + args.writeAtom(msgSequence); + writeMailboxName(args, mbox); + + Response[] r = command("COPY", args); + + // dispatch untagged responses + notifyResponseHandlers(r); + + // Handle result of this command + handleResult(r[r.length-1]); + + if (uid) + return getCopyUID(r); + else + return null; + } + + /** + * MOVE command. + * + * @param msgsets the messages to move + * @param mbox the mailbox to move them to + * @exception ProtocolException for protocol failures + * @see "RFC 6851" + * @since JavaMail 1.5.4 + */ + public void move(MessageSet[] msgsets, String mbox) + throws ProtocolException { + moveuid(MessageSet.toString(msgsets), mbox, false); + } + + /** + * MOVE command. + * + * @param start start message number + * @param end end message number + * @param mbox the mailbox to move them to + * @exception ProtocolException for protocol failures + * @see "RFC 6851" + * @since JavaMail 1.5.4 + */ + public void move(int start, int end, String mbox) + throws ProtocolException { + moveuid(String.valueOf(start) + ":" + String.valueOf(end), + mbox, false); + } + + /** + * MOVE Command, return uid from COPYUID response code. + * + * @param msgsets the messages to move + * @param mbox the mailbox to move them to + * @return COPYUID response data + * @exception ProtocolException for protocol failures + * @see "RFC 6851" + * @see "RFC 4315, section 3" + * @since JavaMail 1.5.4 + */ + public CopyUID moveuid(MessageSet[] msgsets, String mbox) + throws ProtocolException { + return moveuid(MessageSet.toString(msgsets), mbox, true); + } + + /** + * MOVE Command, return uid from COPYUID response code. + * + * @param start start message number + * @param end end message number + * @param mbox the mailbox to move them to + * @return COPYUID response data + * @exception ProtocolException for protocol failures + * @see "RFC 6851" + * @see "RFC 4315, section 3" + * @since JavaMail 1.5.4 + */ + public CopyUID moveuid(int start, int end, String mbox) + throws ProtocolException { + return moveuid(String.valueOf(start) + ":" + String.valueOf(end), + mbox, true); + } + + /** + * MOVE Command, return uid from COPYUID response code. + * + * @see "RFC 6851" + * @see "RFC 4315, section 3" + * @since JavaMail 1.5.4 + */ + private CopyUID moveuid(String msgSequence, String mbox, boolean uid) + throws ProtocolException { + if (!hasCapability("MOVE")) + throw new BadCommandException("MOVE not supported"); + if (uid && !hasCapability("UIDPLUS")) + throw new BadCommandException("UIDPLUS not supported"); + + Argument args = new Argument(); + args.writeAtom(msgSequence); + writeMailboxName(args, mbox); + + Response[] r = command("MOVE", args); + + // dispatch untagged responses + notifyResponseHandlers(r); + + // Handle result of this command + handleResult(r[r.length-1]); + + if (uid) + return getCopyUID(r); + else + return null; + } + + /** + * If the response contains a COPYUID response code, extract + * it and return a CopyUID object with the information. + * + * @param rr the responses to examine + * @return the COPYUID response code data, or null if not found + * @since JavaMail 1.5.4 + */ + protected CopyUID getCopyUID(Response[] rr) { + // most likely in the last response, so start there and work backward + for (int i = rr.length - 1; i >= 0; i--) { + Response r = rr[i]; + if (r == null || !r.isOK()) + continue; + byte b; + while ((b = r.readByte()) > 0 && b != (byte)'[') + ; + if (b == 0) + continue; + String s; + s = r.readAtom(); + if (!s.equalsIgnoreCase("COPYUID")) + continue; + + // XXX - need to merge more than one response for MOVE? + long uidvalidity = r.readLong(); + String src = r.readAtom(); + String dst = r.readAtom(); + return new CopyUID(uidvalidity, + UIDSet.parseUIDSets(src), UIDSet.parseUIDSets(dst)); + } + return null; + } + + public void storeFlags(MessageSet[] msgsets, Flags flags, boolean set) + throws ProtocolException { + storeFlags(MessageSet.toString(msgsets), flags, set); + } + + public void storeFlags(int start, int end, Flags flags, boolean set) + throws ProtocolException { + storeFlags(String.valueOf(start) + ":" + String.valueOf(end), + flags, set); + } + + /** + * Set the specified flags on this message. + * + * @param msg the message number + * @param flags the flags + * @param set true to set, false to clear + * @exception ProtocolException for protocol failures + */ + public void storeFlags(int msg, Flags flags, boolean set) + throws ProtocolException { + storeFlags(String.valueOf(msg), flags, set); + } + + private void storeFlags(String msgset, Flags flags, boolean set) + throws ProtocolException { + Response[] r; + if (set) + r = command("STORE " + msgset + " +FLAGS " + + createFlagList(flags), null); + else + r = command("STORE " + msgset + " -FLAGS " + + createFlagList(flags), null); + + // Dispatch untagged responses + notifyResponseHandlers(r); + handleResult(r[r.length-1]); + } + + /** + * Creates an IMAP flag_list from the given Flags object. + * + * @param flags the flags + * @return the IMAP flag_list + * @since JavaMail 1.5.4 + */ + protected String createFlagList(Flags flags) { + StringBuilder sb = new StringBuilder("("); // start of flag_list + + Flags.Flag[] sf = flags.getSystemFlags(); // get the system flags + boolean first = true; + for (int i = 0; i < sf.length; i++) { + String s; + Flags.Flag f = sf[i]; + if (f == Flags.Flag.ANSWERED) + s = "\\Answered"; + else if (f == Flags.Flag.DELETED) + s = "\\Deleted"; + else if (f == Flags.Flag.DRAFT) + s = "\\Draft"; + else if (f == Flags.Flag.FLAGGED) + s = "\\Flagged"; + else if (f == Flags.Flag.RECENT) + s = "\\Recent"; + else if (f == Flags.Flag.SEEN) + s = "\\Seen"; + else + continue; // skip it + if (first) + first = false; + else + sb.append(' '); + sb.append(s); + } + + String[] uf = flags.getUserFlags(); // get the user flag strings + for (int i = 0; i < uf.length; i++) { + if (first) + first = false; + else + sb.append(' '); + sb.append(uf[i]); + } + + sb.append(")"); // terminate flag_list + return sb.toString(); + } + + /** + * Issue the given search criterion on the specified message sets. + * Returns array of matching sequence numbers. An empty array + * is returned if no matches are found. + * + * @param msgsets array of MessageSets + * @param term SearchTerm + * @return array of matching sequence numbers. + * @exception ProtocolException for protocol failures + * @exception SearchException for search failures + */ + public int[] search(MessageSet[] msgsets, SearchTerm term) + throws ProtocolException, SearchException { + return search(MessageSet.toString(msgsets), term); + } + + /** + * Issue the given search criterion on all messages in this folder. + * Returns array of matching sequence numbers. An empty array + * is returned if no matches are found. + * + * @param term SearchTerm + * @return array of matching sequence numbers. + * @exception ProtocolException for protocol failures + * @exception SearchException for search failures + */ + public int[] search(SearchTerm term) + throws ProtocolException, SearchException { + return search("ALL", term); + } + + /* + * Apply the given SearchTerm on the specified sequence. + * Returns array of matching sequence numbers. Note that an empty + * array is returned for no matches. + */ + private int[] search(String msgSequence, SearchTerm term) + throws ProtocolException, SearchException { + // Check if the search "text" terms contain only ASCII chars, + // or if utf8 support has been enabled (in which case CHARSET + // is not allowed; see RFC 6855, section 3, last paragraph) + if (supportsUtf8() || SearchSequence.isAscii(term)) { + try { + return issueSearch(msgSequence, term, null); + } catch (IOException ioex) { /* will not happen */ } + } + + /* + * The search "text" terms do contain non-ASCII chars and utf8 + * support has not been enabled. We need to use: + * "SEARCH CHARSET ..." + * The charsets we try to use are UTF-8 and the locale's + * default charset. If the server supports UTF-8, great, + * always use it. Else we try to use the default charset. + */ + + // Cycle thru the list of charsets + for (int i = 0; i < searchCharsets.length; i++) { + if (searchCharsets[i] == null) + continue; + + try { + return issueSearch(msgSequence, term, searchCharsets[i]); + } catch (CommandFailedException cfx) { + /* + * Server returned NO. For now, I'll just assume that + * this indicates that this charset is unsupported. + * We can check the BADCHARSET response code once + * that's spec'd into the IMAP RFC .. + */ + searchCharsets[i] = null; + continue; + } catch (IOException ioex) { + /* Charset conversion failed. Try the next one */ + continue; + } catch (ProtocolException pex) { + throw pex; + } catch (SearchException sex) { + throw sex; + } + } + + // No luck. + throw new SearchException("Search failed"); + } + + /* Apply the given SearchTerm on the specified sequence, using the + * given charset.

    + * Returns array of matching sequence numbers. Note that an empty + * array is returned for no matches. + */ + private int[] issueSearch(String msgSequence, SearchTerm term, + String charset) + throws ProtocolException, SearchException, IOException { + + // Generate a search-sequence with the given charset + Argument args = getSearchSequence().generateSequence(term, + charset == null ? null : + MimeUtility.javaCharset(charset) + ); + args.writeAtom(msgSequence); + + Response[] r; + + if (charset == null) // text is all US-ASCII + r = command("SEARCH", args); + else + r = command("SEARCH CHARSET " + charset, args); + + Response response = r[r.length-1]; + int[] matches = null; + + // Grab all SEARCH responses + if (response.isOK()) { // command succesful + List v = new ArrayList<>(); + int num; + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + // There *will* be one SEARCH response. + if (ir.keyEquals("SEARCH")) { + while ((num = ir.readNumber()) != -1) + v.add(Integer.valueOf(num)); + r[i] = null; + } + } + + // Copy the list into 'matches' + int vsize = v.size(); + matches = new int[vsize]; + for (int i = 0; i < vsize; i++) + matches[i] = v.get(i).intValue(); + } + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + return matches; + } + + /** + * Get the SearchSequence object. + * The SearchSequence object instance is saved in the searchSequence + * field. Subclasses of IMAPProtocol may override this method to + * return a subclass of SearchSequence, in order to add support for + * product-specific search terms. + * + * @return the SearchSequence + * @since JavaMail 1.4.6 + */ + protected SearchSequence getSearchSequence() { + if (searchSequence == null) + searchSequence = new SearchSequence(this); + return searchSequence; + } + + /** + * Sort messages in the folder according to the specified sort criteria. + * If the search term is not null, limit the sort to only the messages + * that match the search term. + * Returns an array of sorted sequence numbers. An empty array + * is returned if no matches are found. + * + * @param term sort criteria + * @param sterm SearchTerm + * @return array of matching sequence numbers. + * @exception ProtocolException for protocol failures + * @exception SearchException for search failures + * + * @see "RFC 5256" + * @since JavaMail 1.4.4 + */ + public int[] sort(SortTerm[] term, SearchTerm sterm) + throws ProtocolException, SearchException { + if (!hasCapability("SORT*")) + throw new BadCommandException("SORT not supported"); + + if (term == null || term.length == 0) + throw new BadCommandException("Must have at least one sort term"); + + Argument args = new Argument(); + Argument sargs = new Argument(); + for (int i = 0; i < term.length; i++) + sargs.writeAtom(term[i].toString()); + args.writeArgument(sargs); // sort criteria + + args.writeAtom("UTF-8"); // charset specification + if (sterm != null) { + try { + args.append( + getSearchSequence().generateSequence(sterm, "UTF-8")); + } catch (IOException ioex) { + // should never happen + throw new SearchException(ioex.toString()); + } + } else + args.writeAtom("ALL"); + + Response[] r = command("SORT", args); + Response response = r[r.length-1]; + int[] matches = null; + + // Grab all SORT responses + if (response.isOK()) { // command succesful + List v = new ArrayList<>(); + int num; + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals("SORT")) { + while ((num = ir.readNumber()) != -1) + v.add(Integer.valueOf(num)); + r[i] = null; + } + } + + // Copy the list into 'matches' + int vsize = v.size(); + matches = new int[vsize]; + for (int i = 0; i < vsize; i++) + matches[i] = v.get(i).intValue(); + } + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + return matches; + } + + /** + * NAMESPACE Command. + * + * @return the namespaces + * @exception ProtocolException for protocol failures + * @see "RFC2342" + */ + public Namespaces namespace() throws ProtocolException { + if (!hasCapability("NAMESPACE")) + throw new BadCommandException("NAMESPACE not supported"); + + Response[] r = command("NAMESPACE", null); + + Namespaces namespace = null; + Response response = r[r.length-1]; + + // Grab NAMESPACE response + if (response.isOK()) { // command succesful + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals("NAMESPACE")) { + if (namespace == null) + namespace = new Namespaces(ir); + r[i] = null; + } + } + } + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + return namespace; + } + + /** + * GETQUOTAROOT Command. + * + * Returns an array of Quota objects, representing the quotas + * for this mailbox and, indirectly, the quotaroots for this + * mailbox. + * + * @param mbox the mailbox + * @return array of Quota objects + * @exception ProtocolException for protocol failures + * @see "RFC2087" + */ + public Quota[] getQuotaRoot(String mbox) throws ProtocolException { + if (!hasCapability("QUOTA")) + throw new BadCommandException("GETQUOTAROOT not supported"); + + Argument args = new Argument(); + writeMailboxName(args, mbox); + + Response[] r = command("GETQUOTAROOT", args); + + Response response = r[r.length-1]; + + Map tab = new HashMap<>(); + + // Grab all QUOTAROOT and QUOTA responses + if (response.isOK()) { // command succesful + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals("QUOTAROOT")) { + // quotaroot_response + // ::= "QUOTAROOT" SP astring *(SP astring) + + // read name of mailbox and throw away + ir.readAtomString(); + // for each quotaroot add a placeholder quota + String root = null; + while ((root = ir.readAtomString()) != null && + root.length() > 0) + tab.put(root, new Quota(root)); + r[i] = null; + } else if (ir.keyEquals("QUOTA")) { + Quota quota = parseQuota(ir); + Quota q = tab.get(quota.quotaRoot); + if (q != null && q.resources != null) { + // merge resources + int newl = q.resources.length + quota.resources.length; + Quota.Resource[] newr = new Quota.Resource[newl]; + System.arraycopy(q.resources, 0, newr, 0, + q.resources.length); + System.arraycopy(quota.resources, 0, + newr, q.resources.length, quota.resources.length); + quota.resources = newr; + } + tab.put(quota.quotaRoot, quota); + r[i] = null; + } + } + } + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + + return tab.values().toArray(new Quota[tab.size()]); + } + + /** + * GETQUOTA Command. + * + * Returns an array of Quota objects, representing the quotas + * for this quotaroot. + * + * @param root the quotaroot + * @return the quotas + * @exception ProtocolException for protocol failures + * @see "RFC2087" + */ + public Quota[] getQuota(String root) throws ProtocolException { + if (!hasCapability("QUOTA")) + throw new BadCommandException("QUOTA not supported"); + + Argument args = new Argument(); + args.writeString(root); // XXX - could be UTF-8? + + Response[] r = command("GETQUOTA", args); + + Quota quota = null; + List v = new ArrayList<>(); + Response response = r[r.length-1]; + + // Grab all QUOTA responses + if (response.isOK()) { // command succesful + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals("QUOTA")) { + quota = parseQuota(ir); + v.add(quota); + r[i] = null; + } + } + } + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + return v.toArray(new Quota[v.size()]); + } + + /** + * SETQUOTA Command. + * + * Set the indicated quota on the corresponding quotaroot. + * + * @param quota the quota to set + * @exception ProtocolException for protocol failures + * @see "RFC2087" + */ + public void setQuota(Quota quota) throws ProtocolException { + if (!hasCapability("QUOTA")) + throw new BadCommandException("QUOTA not supported"); + + Argument args = new Argument(); + args.writeString(quota.quotaRoot); // XXX - could be UTF-8? + Argument qargs = new Argument(); + if (quota.resources != null) { + for (int i = 0; i < quota.resources.length; i++) { + qargs.writeAtom(quota.resources[i].name); + qargs.writeNumber(quota.resources[i].limit); + } + } + args.writeArgument(qargs); + + Response[] r = command("SETQUOTA", args); + Response response = r[r.length-1]; + + // XXX - It's not clear from the RFC whether the SETQUOTA command + // will provoke untagged QUOTA responses. If it does, perhaps + // we should grab them here and return them? + + /* + Quota quota = null; + List v = new ArrayList(); + + // Grab all QUOTA responses + if (response.isOK()) { // command succesful + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals("QUOTA")) { + quota = parseQuota(ir); + v.add(quota); + r[i] = null; + } + } + } + */ + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + /* + return v.toArray(new Quota[v.size()]); + */ + } + + /** + * Parse a QUOTA response. + */ + private Quota parseQuota(Response r) throws ParsingException { + // quota_response ::= "QUOTA" SP astring SP quota_list + String quotaRoot = r.readAtomString(); // quotaroot ::= astring + Quota q = new Quota(quotaRoot); + r.skipSpaces(); + // quota_list ::= "(" #quota_resource ")" + if (r.readByte() != '(') + throw new ParsingException("parse error in QUOTA"); + + List v = new ArrayList<>(); + while (!r.isNextNonSpace(')')) { + // quota_resource ::= atom SP number SP number + String name = r.readAtom(); + if (name != null) { + long usage = r.readLong(); + long limit = r.readLong(); + Quota.Resource res = new Quota.Resource(name, usage, limit); + v.add(res); + } + } + q.resources = v.toArray(new Quota.Resource[v.size()]); + return q; + } + + + /** + * SETACL Command. + * + * @param mbox the mailbox + * @param modifier the ACL modifier + * @param acl the ACL + * @exception ProtocolException for protocol failures + * @see "RFC2086" + */ + public void setACL(String mbox, char modifier, ACL acl) + throws ProtocolException { + if (!hasCapability("ACL")) + throw new BadCommandException("ACL not supported"); + + Argument args = new Argument(); + writeMailboxName(args, mbox); + args.writeString(acl.getName()); + String rights = acl.getRights().toString(); + if (modifier == '+' || modifier == '-') + rights = modifier + rights; + args.writeString(rights); + + Response[] r = command("SETACL", args); + Response response = r[r.length-1]; + + // dispatch untagged responses + notifyResponseHandlers(r); + handleResult(response); + } + + /** + * DELETEACL Command. + * + * @param mbox the mailbox + * @param user the user + * @exception ProtocolException for protocol failures + * @see "RFC2086" + */ + public void deleteACL(String mbox, String user) throws ProtocolException { + if (!hasCapability("ACL")) + throw new BadCommandException("ACL not supported"); + + Argument args = new Argument(); + writeMailboxName(args, mbox); + args.writeString(user); // XXX - could be UTF-8? + + Response[] r = command("DELETEACL", args); + Response response = r[r.length-1]; + + // dispatch untagged responses + notifyResponseHandlers(r); + handleResult(response); + } + + /** + * GETACL Command. + * + * @param mbox the mailbox + * @return the ACL array + * @exception ProtocolException for protocol failures + * @see "RFC2086" + */ + public ACL[] getACL(String mbox) throws ProtocolException { + if (!hasCapability("ACL")) + throw new BadCommandException("ACL not supported"); + + Argument args = new Argument(); + writeMailboxName(args, mbox); + + Response[] r = command("GETACL", args); + Response response = r[r.length-1]; + + // Grab all ACL responses + List v = new ArrayList<>(); + if (response.isOK()) { // command succesful + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals("ACL")) { + // acl_data ::= "ACL" SPACE mailbox + // *(SPACE identifier SPACE rights) + // read name of mailbox and throw away + ir.readAtomString(); + String name = null; + while ((name = ir.readAtomString()) != null) { + String rights = ir.readAtomString(); + if (rights == null) + break; + ACL acl = new ACL(name, new Rights(rights)); + v.add(acl); + } + r[i] = null; + } + } + } + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + return v.toArray(new ACL[v.size()]); + } + + /** + * LISTRIGHTS Command. + * + * @param mbox the mailbox + * @param user the user rights to return + * @return the rights array + * @exception ProtocolException for protocol failures + * @see "RFC2086" + */ + public Rights[] listRights(String mbox, String user) + throws ProtocolException { + if (!hasCapability("ACL")) + throw new BadCommandException("ACL not supported"); + + Argument args = new Argument(); + writeMailboxName(args, mbox); + args.writeString(user); // XXX - could be UTF-8? + + Response[] r = command("LISTRIGHTS", args); + Response response = r[r.length-1]; + + // Grab LISTRIGHTS response + List v = new ArrayList<>(); + if (response.isOK()) { // command succesful + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals("LISTRIGHTS")) { + // listrights_data ::= "LISTRIGHTS" SPACE mailbox + // SPACE identifier SPACE rights *(SPACE rights) + // read name of mailbox and throw away + ir.readAtomString(); + // read identifier and throw away + ir.readAtomString(); + String rights; + while ((rights = ir.readAtomString()) != null) + v.add(new Rights(rights)); + r[i] = null; + } + } + } + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + return v.toArray(new Rights[v.size()]); + } + + /** + * MYRIGHTS Command. + * + * @param mbox the mailbox + * @return the rights + * @exception ProtocolException for protocol failures + * @see "RFC2086" + */ + public Rights myRights(String mbox) throws ProtocolException { + if (!hasCapability("ACL")) + throw new BadCommandException("ACL not supported"); + + Argument args = new Argument(); + writeMailboxName(args, mbox); + + Response[] r = command("MYRIGHTS", args); + Response response = r[r.length-1]; + + // Grab MYRIGHTS response + Rights rights = null; + if (response.isOK()) { // command succesful + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals("MYRIGHTS")) { + // myrights_data ::= "MYRIGHTS" SPACE mailbox SPACE rights + // read name of mailbox and throw away + ir.readAtomString(); + String rs = ir.readAtomString(); + if (rights == null) + rights = new Rights(rs); + r[i] = null; + } + } + } + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + return rights; + } + + /* + * The tag used on the IDLE command. Set by idleStart() and + * used in processIdleResponse() to determine if the response + * is the matching end tag. + */ + private volatile String idleTag; + + /** + * IDLE Command.

    + * + * If the server supports the IDLE command extension, the IDLE + * command is issued and this method blocks until a response has + * been received. Once the first response has been received, the + * IDLE command is terminated and all responses are collected and + * handled and this method returns.

    + * + * Note that while this method is blocked waiting for a response, + * no other threads may issue any commands to the server that would + * use this same connection. + * + * @exception ProtocolException for protocol failures + * @see "RFC2177" + * @since JavaMail 1.4.1 + */ + public synchronized void idleStart() throws ProtocolException { + if (!hasCapability("IDLE")) + throw new BadCommandException("IDLE not supported"); + + List v = new ArrayList<>(); + boolean done = false; + Response r = null; + + // write the command + try { + idleTag = writeCommand("IDLE", null); + } catch (LiteralException lex) { + v.add(lex.getResponse()); + done = true; + } catch (Exception ex) { + // Convert this into a BYE response + v.add(Response.byeResponse(ex)); + done = true; + } + + while (!done) { + try { + r = readResponse(); + } catch (IOException ioex) { + // convert this into a BYE response + r = Response.byeResponse(ioex); + } catch (ProtocolException pex) { + continue; // skip this response + } + + v.add(r); + + if (r.isContinuation() || r.isBYE()) + done = true; + } + + Response[] responses = v.toArray(new Response[v.size()]); + r = responses[responses.length-1]; + + // dispatch remaining untagged responses + notifyResponseHandlers(responses); + if (!r.isContinuation()) + handleResult(r); + } + + /** + * While an IDLE command is in progress, read a response + * sent from the server. The response is read with no locks + * held so that when the read blocks waiting for the response + * from the server it's not holding locks that would prevent + * other threads from interrupting the IDLE command. + * + * @return the response + * @since JavaMail 1.4.1 + */ + public synchronized Response readIdleResponse() { + if (idleTag == null) + return null; // IDLE not in progress + Response r = null; + try { + r = readResponse(); + } catch (IOException ioex) { + // convert this into a BYE response + r = Response.byeResponse(ioex); + } catch (ProtocolException pex) { + // convert this into a BYE response + r = Response.byeResponse(pex); + } + return r; + } + + /** + * Process a response returned by readIdleResponse(). + * This method will be called with appropriate locks + * held so that the processing of the response is safe. + * + * @param r the response + * @return true if IDLE is done + * @exception ProtocolException for protocol failures + * @since JavaMail 1.4.1 + */ + public boolean processIdleResponse(Response r) throws ProtocolException { + Response[] responses = new Response[1]; + responses[0] = r; + boolean done = false; // done reading responses? + notifyResponseHandlers(responses); + + if (r.isBYE()) // shouldn't wait for command completion response + done = true; + + // If this is a matching command completion response, we are done + if (r.isTagged() && r.getTag().equals(idleTag)) + done = true; + + if (done) + idleTag = null; // no longer in IDLE + + handleResult(r); + return !done; + } + + // the DONE command to break out of IDLE + private static final byte[] DONE = { 'D', 'O', 'N', 'E', '\r', '\n' }; + + /** + * Abort an IDLE command. While one thread is blocked in + * readIdleResponse(), another thread will use this method + * to abort the IDLE command, which will cause the server + * to send the closing tag for the IDLE command, which + * readIdleResponse() and processIdleResponse() will see + * and terminate the IDLE state. + * + * @since JavaMail 1.4.1 + */ + public void idleAbort() { + OutputStream os = getOutputStream(); + try { + os.write(DONE); + os.flush(); + } catch (Exception ex) { + // nothing to do, hope to detect it again later + logger.log(Level.FINEST, "Exception aborting IDLE", ex); + } + } + + /** + * ID Command. + * + * @param clientParams map of names and values + * @return map of names and values from server + * @exception ProtocolException for protocol failures + * @see "RFC 2971" + * @since JavaMail 1.5.1 + */ + public Map id(Map clientParams) + throws ProtocolException { + if (!hasCapability("ID")) + throw new BadCommandException("ID not supported"); + + Response[] r = command("ID", ID.getArgumentList(clientParams)); + + ID id = null; + Response response = r[r.length-1]; + + // Grab ID response + if (response.isOK()) { // command succesful + for (int i = 0, len = r.length; i < len; i++) { + if (!(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + if (ir.keyEquals("ID")) { + if (id == null) + id = new ID(ir); + r[i] = null; + } + } + } + + // dispatch remaining untagged responses + notifyResponseHandlers(r); + handleResult(response); + return id == null ? null : id.getServerParams(); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPReferralException.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPReferralException.java new file mode 100644 index 000000000..393b1ed08 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPReferralException.java @@ -0,0 +1,77 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import com.sun.mail.iap.ProtocolException; + +/** + * A ProtocolException that includes IMAP login referral information. + * + * @since JavaMail 1.5.5 + */ + +public class IMAPReferralException extends ProtocolException { + + private String url; + + private static final long serialVersionUID = 2578770669364251968L; + + /** + * Constructs an IMAPReferralException with the specified detail message. + * and URL. + * + * @param s the detail message + * @param url the URL + */ + public IMAPReferralException(String s, String url) { + super(s); + this.url = url; + } + + /** + * Return the IMAP URL in the referral. + * + * @return the IMAP URL + */ + public String getUrl() { + return url; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPResponse.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPResponse.java new file mode 100644 index 000000000..6ccf0cadb --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPResponse.java @@ -0,0 +1,164 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.io.*; +import java.util.*; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.iap.*; + +/** + * This class represents a response obtained from the input stream + * of an IMAP server. + * + * @author John Mani + */ + +public class IMAPResponse extends Response { + private String key; + private int number; + + public IMAPResponse(Protocol c) throws IOException, ProtocolException { + super(c); + init(); + } + + private void init() throws IOException, ProtocolException { + // continue parsing if this is an untagged response + if (isUnTagged() && !isOK() && !isNO() && !isBAD() && !isBYE()) { + key = readAtom(); + + // Is this response of the form "* " + try { + number = Integer.parseInt(key); + key = readAtom(); + } catch (NumberFormatException ne) { } + } + } + + /** + * Copy constructor. + * + * @param r the IMAPResponse to copy + */ + public IMAPResponse(IMAPResponse r) { + super((Response)r); + key = r.key; + number = r.number; + } + + /** + * For testing. + * + * @param r the response string + * @exception IOException for I/O errors + * @exception ProtocolException for protocol failures + */ + public IMAPResponse(String r) throws IOException, ProtocolException { + this(r, true); + } + + /** + * For testing. + * + * @param r the response string + * @param utf8 UTF-8 allowed? + * @exception IOException for I/O errors + * @exception ProtocolException for protocol failures + * @since JavaMail 1.6.0 + */ + public IMAPResponse(String r, boolean utf8) + throws IOException, ProtocolException { + super(r, utf8); + init(); + } + + /** + * Read a list of space-separated "flag-extension" sequences and + * return the list as a array of Strings. An empty list is returned + * as null. Each item is expected to be an atom, possibly preceeded + * by a backslash, but we aren't that strict; we just look for strings + * separated by spaces and terminated by a right paren. We assume items + * are always ASCII. + * + * @return the list items as a String array + */ + public String[] readSimpleList() { + skipSpaces(); + + if (buffer[index] != '(') // not what we expected + return null; + index++; // skip '(' + + List v = new ArrayList<>(); + int start; + for (start = index; buffer[index] != ')'; index++) { + if (buffer[index] == ' ') { // got one item + v.add(ASCIIUtility.toString(buffer, start, index)); + start = index+1; // index gets incremented at the top + } + } + if (index > start) // get the last item + v.add(ASCIIUtility.toString(buffer, start, index)); + index++; // skip ')' + + int size = v.size(); + if (size > 0) + return v.toArray(new String[size]); + else // empty list + return null; + } + + public String getKey() { + return key; + } + + public boolean keyEquals(String k) { + if (key != null && key.equalsIgnoreCase(k)) + return true; + else + return false; + } + + public int getNumber() { + return number; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPSaslAuthenticator.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPSaslAuthenticator.java new file mode 100644 index 000000000..d8df5a9b3 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/IMAPSaslAuthenticator.java @@ -0,0 +1,308 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.io.*; +import java.util.*; +import java.util.logging.Level; +import javax.security.sasl.*; +import javax.security.auth.callback.*; + +import com.sun.mail.iap.*; +import com.sun.mail.imap.*; +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.MailLogger; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.util.BASE64EncoderStream; +import com.sun.mail.util.BASE64DecoderStream; + +/** + * This class contains a single method that does authentication using + * SASL. This is in a separate class so that it can be compiled with + * J2SE 1.5. Eventually it should be merged into IMAPProtocol.java. + */ + +public class IMAPSaslAuthenticator implements SaslAuthenticator { + + private IMAPProtocol pr; + private String name; + private Properties props; + private MailLogger logger; + private String host; + + /* + * This is a hack to initialize the OAUTH SASL provider just before, + * and only if, we might need it. This avoids the need for the user + * to initialize it explicitly, or manually configure the security + * providers file. + */ + static { + try { + com.sun.mail.auth.OAuth2SaslClientFactory.init(); + } catch (Throwable t) { } + } + + public IMAPSaslAuthenticator(IMAPProtocol pr, String name, Properties props, + MailLogger logger, String host) { + this.pr = pr; + this.name = name; + this.props = props; + this.logger = logger; + this.host = host; + } + + @Override + public boolean authenticate(String[] mechs, final String realm, + final String authzid, final String u, + final String p) throws ProtocolException { + + synchronized (pr) { // authenticate method should be synchronized + List v = new ArrayList<>(); + String tag = null; + Response r = null; + boolean done = false; + if (logger.isLoggable(Level.FINE)) { + logger.fine("SASL Mechanisms:"); + for (int i = 0; i < mechs.length; i++) + logger.fine(" " + mechs[i]); + logger.fine(""); + } + + SaslClient sc; + CallbackHandler cbh = new CallbackHandler() { + @Override + public void handle(Callback[] callbacks) { + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL callback length: " + callbacks.length); + for (int i = 0; i < callbacks.length; i++) { + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL callback " + i + ": " + callbacks[i]); + if (callbacks[i] instanceof NameCallback) { + NameCallback ncb = (NameCallback)callbacks[i]; + ncb.setName(u); + } else if (callbacks[i] instanceof PasswordCallback) { + PasswordCallback pcb = (PasswordCallback)callbacks[i]; + pcb.setPassword(p.toCharArray()); + } else if (callbacks[i] instanceof RealmCallback) { + RealmCallback rcb = (RealmCallback)callbacks[i]; + rcb.setText(realm != null ? + realm : rcb.getDefaultText()); + } else if (callbacks[i] instanceof RealmChoiceCallback) { + RealmChoiceCallback rcb = + (RealmChoiceCallback)callbacks[i]; + if (realm == null) + rcb.setSelectedIndex(rcb.getDefaultChoice()); + else { + // need to find specified realm in list + String[] choices = rcb.getChoices(); + for (int k = 0; k < choices.length; k++) { + if (choices[k].equals(realm)) { + rcb.setSelectedIndex(k); + break; + } + } + } + } + } + } + }; + + try { + @SuppressWarnings("unchecked") + Map propsMap = (Map) props; + sc = Sasl.createSaslClient(mechs, authzid, name, host, + propsMap, cbh); + } catch (SaslException sex) { + logger.log(Level.FINE, "Failed to create SASL client", sex); + throw new UnsupportedOperationException(sex.getMessage(), sex); + } + if (sc == null) { + logger.fine("No SASL support"); + throw new UnsupportedOperationException("No SASL support"); + } + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL client " + sc.getMechanismName()); + + try { + Argument args = new Argument(); + args.writeAtom(sc.getMechanismName()); + if (pr.hasCapability("SASL-IR") && sc.hasInitialResponse()) { + String irs; + byte[] ba = sc.evaluateChallenge(new byte[0]); + if (ba.length > 0) { + ba = BASE64EncoderStream.encode(ba); + irs = ASCIIUtility.toString(ba, 0, ba.length); + } else + irs = "="; + args.writeAtom(irs); + } + tag = pr.writeCommand("AUTHENTICATE", args); + } catch (Exception ex) { + logger.log(Level.FINE, "SASL AUTHENTICATE Exception", ex); + return false; + } + + OutputStream os = pr.getIMAPOutputStream(); // stream to IMAP server + + /* + * Wrap a BASE64Encoder around a ByteArrayOutputstream + * to craft b64 encoded username and password strings + * + * Note that the encoded bytes should be sent "as-is" to the + * server, *not* as literals or quoted-strings. + * + * Also note that unlike the B64 definition in MIME, CRLFs + * should *not* be inserted during the encoding process. So, I + * use Integer.MAX_VALUE (0x7fffffff (> 1G)) as the bytesPerLine, + * which should be sufficiently large ! + */ + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] CRLF = { (byte)'\r', (byte)'\n'}; + + // Hack for Novell GroupWise XGWTRUSTEDAPP authentication mechanism + // http://www.novell.com/developer/documentation/gwimap/? + // page=/developer/documentation/gwimap/gwimpenu/data/al7te9j.html + boolean isXGWTRUSTEDAPP = + sc.getMechanismName().equals("XGWTRUSTEDAPP") && + PropUtil.getBooleanProperty(props, + "mail." + name + ".sasl.xgwtrustedapphack.enable", true); + while (!done) { // loop till we are done + try { + r = pr.readResponse(); + if (r.isContinuation()) { + byte[] ba = null; + if (!sc.isComplete()) { + ba = r.readByteArray().getNewBytes(); + if (ba.length > 0) + ba = BASE64DecoderStream.decode(ba); + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL challenge: " + + ASCIIUtility.toString(ba, 0, ba.length) + " :"); + ba = sc.evaluateChallenge(ba); + } + if (ba == null) { + logger.fine("SASL no response"); + os.write(CRLF); // write out empty line + os.flush(); // flush the stream + bos.reset(); // reset buffer + } else { + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL response: " + + ASCIIUtility.toString(ba, 0, ba.length) + " :"); + ba = BASE64EncoderStream.encode(ba); + if (isXGWTRUSTEDAPP) + bos.write(ASCIIUtility.getBytes("XGWTRUSTEDAPP ")); + bos.write(ba); + + bos.write(CRLF); // CRLF termination + os.write(bos.toByteArray()); // write out line + os.flush(); // flush the stream + bos.reset(); // reset buffer + } + } else if (r.isTagged() && r.getTag().equals(tag)) + // Ah, our tagged response + done = true; + else if (r.isBYE()) // outta here + done = true; + else // hmm .. unsolicited response here ?! + v.add(r); + } catch (Exception ioex) { + logger.log(Level.FINE, "SASL Exception", ioex); + // convert this into a BYE response + r = Response.byeResponse(ioex); + done = true; + // XXX - ultimately return true??? + } + } + + if (sc.isComplete() /*&& res.status == SUCCESS*/) { + String qop = (String)sc.getNegotiatedProperty(Sasl.QOP); + if (qop != null && (qop.equalsIgnoreCase("auth-int") || + qop.equalsIgnoreCase("auth-conf"))) { + // XXX - NOT SUPPORTED!!! + logger.fine( + "SASL Mechanism requires integrity or confidentiality"); + return false; + } + } + + Response[] responses = v.toArray(new Response[v.size()]); + + // handle an illegal but not uncommon untagged CAPABILTY response + pr.handleCapabilityResponse(responses); + + /* + * Dispatch untagged responses. + * NOTE: in our current upper level IMAP classes, we add the + * responseHandler to the Protocol object only *after* the + * connection has been authenticated. So, for now, the below + * code really ends up being just a no-op. + */ + pr.notifyResponseHandlers(responses); + + // Handle the final OK, NO, BAD or BYE response + pr.handleLoginResult(r); + pr.setCapabilities(r); + + /* + * If we're using the Novell Groupwise XGWTRUSTEDAPP mechanism + * to run as a specified authorization ID, we have to issue a + * LOGIN command to select the user we want to operate as. + */ + if (isXGWTRUSTEDAPP && authzid != null) { + Argument args = new Argument(); + args.writeString(authzid); + + responses = pr.command("LOGIN", args); + + // dispatch untagged responses + pr.notifyResponseHandlers(responses); + + // Handle result of this command + pr.handleResult(responses[responses.length-1]); + // If the response includes a CAPABILITY response code, process it + pr.setCapabilities(responses[responses.length-1]); + } + return true; + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/INTERNALDATE.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/INTERNALDATE.java new file mode 100644 index 000000000..3304e0640 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/INTERNALDATE.java @@ -0,0 +1,152 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.Date; +import java.util.TimeZone; +import java.util.Locale; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.text.FieldPosition; + +import javax.mail.internet.MailDateFormat; + +import com.sun.mail.iap.*; + + +/** + * An INTERNALDATE FETCH item. + * + * @author John Mani + */ + +public class INTERNALDATE implements Item { + + static final char[] name = + {'I','N','T','E','R','N','A','L','D','A','T','E'}; + public int msgno; + protected Date date; + + /* + * Used to parse dates only. The parse method is thread safe + * so we only need to create a single object for use by all + * instances. We depend on the fact that the MailDateFormat + * class will parse dates in INTERNALDATE format as well as + * dates in RFC 822 format. + */ + private static final MailDateFormat mailDateFormat = new MailDateFormat(); + + /** + * Constructor. + * + * @param r the FetchResponse + * @exception ParsingException for parsing failures + */ + public INTERNALDATE(FetchResponse r) throws ParsingException { + msgno = r.getNumber(); + r.skipSpaces(); + String s = r.readString(); + if (s == null) + throw new ParsingException("INTERNALDATE is NIL"); + try { + synchronized (mailDateFormat) { + date = mailDateFormat.parse(s); + } + } catch (ParseException pex) { + throw new ParsingException("INTERNALDATE parse error"); + } + } + + public Date getDate() { + return date; + } + + // INTERNALDATE formatter + + private static SimpleDateFormat df = + // Need Locale.US, the "MMM" field can produce unexpected values + // in non US locales ! + new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss ", Locale.US); + + /** + * Format given Date object into INTERNALDATE string + * + * @param d the Date + * @return INTERNALDATE string + */ + public static String format(Date d) { + /* + * SimpleDateFormat objects aren't thread safe, so rather + * than create a separate such object for each request, + * we create one object and synchronize its use here + * so that only one thread is using it at a time. This + * trades off some potential concurrency for speed in the + * common case. + * + * This method is only used when formatting the date in a + * message that's being appended to a folder. + */ + StringBuffer sb = new StringBuffer(); + synchronized (df) { + df.format(d, sb, new FieldPosition(0)); + } + + // compute timezone offset string + TimeZone tz = TimeZone.getDefault(); + int offset = tz.getOffset(d.getTime()); // get offset from GMT + int rawOffsetInMins = offset / 60 / 1000; // offset from GMT in mins + if (rawOffsetInMins < 0) { + sb.append('-'); + rawOffsetInMins = (-rawOffsetInMins); + } else + sb.append('+'); + + int offsetInHrs = rawOffsetInMins / 60; + int offsetInMins = rawOffsetInMins % 60; + + sb.append(Character.forDigit((offsetInHrs/10), 10)); + sb.append(Character.forDigit((offsetInHrs%10), 10)); + sb.append(Character.forDigit((offsetInMins/10), 10)); + sb.append(Character.forDigit((offsetInMins%10), 10)); + + return sb.toString(); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/Item.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/Item.java new file mode 100644 index 000000000..3aa83df33 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/Item.java @@ -0,0 +1,54 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +/** + * A tagging interface for all IMAP data items. + * Note that the "name" field of all IMAP items MUST be in uppercase.

    + * + * See the BODY, BODYSTRUCTURE, ENVELOPE, FLAGS, INTERNALDATE, RFC822DATA, + * RFC822SIZE, and UID classes. + * + * @author John Mani + */ + +public interface Item { +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/ListInfo.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/ListInfo.java new file mode 100644 index 000000000..9571aefe9 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/ListInfo.java @@ -0,0 +1,103 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.List; +import java.util.ArrayList; + +import com.sun.mail.iap.*; + +/** + * A LIST response. + * + * @author John Mani + * @author Bill Shannon + */ + +public class ListInfo { + public String name = null; + public char separator = '/'; + public boolean hasInferiors = true; + public boolean canOpen = true; + public int changeState = INDETERMINATE; + public String[] attrs; + + public static final int CHANGED = 1; + public static final int UNCHANGED = 2; + public static final int INDETERMINATE = 3; + + public ListInfo(IMAPResponse r) throws ParsingException { + String[] s = r.readSimpleList(); + + List v = new ArrayList<>(); // accumulate attributes + if (s != null) { + // non-empty attribute list + for (int i = 0; i < s.length; i++) { + if (s[i].equalsIgnoreCase("\\Marked")) + changeState = CHANGED; + else if (s[i].equalsIgnoreCase("\\Unmarked")) + changeState = UNCHANGED; + else if (s[i].equalsIgnoreCase("\\Noselect")) + canOpen = false; + else if (s[i].equalsIgnoreCase("\\Noinferiors")) + hasInferiors = false; + v.add(s[i]); + } + } + attrs = v.toArray(new String[v.size()]); + + r.skipSpaces(); + if (r.readByte() == '"') { + if ((separator = (char)r.readByte()) == '\\') + // escaped separator character + separator = (char)r.readByte(); + r.skip(1); // skip <"> + } else // NIL + r.skip(2); + + r.skipSpaces(); + name = r.readAtomString(); + + if (!r.supportsUtf8()) + // decode the name (using RFC2060's modified UTF7) + name = BASE64MailboxDecoder.decode(name); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/MODSEQ.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/MODSEQ.java new file mode 100644 index 000000000..a1b285b75 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/MODSEQ.java @@ -0,0 +1,77 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import com.sun.mail.iap.*; + +/** + * This class represents the MODSEQ data item. + * + * @since JavaMail 1.5.1 + * @author Bill Shannon + */ + +public class MODSEQ implements Item { + + static final char[] name = {'M','O','D','S','E','Q'}; + public int seqnum; + + public long modseq; + + /** + * Constructor. + * + * @param r the FetchResponse + * @exception ParsingException for parsing failures + */ + public MODSEQ(FetchResponse r) throws ParsingException { + seqnum = r.getNumber(); + r.skipSpaces(); + + if (r.readByte() != '(') + throw new ParsingException("MODSEQ parse error"); + + modseq = r.readLong(); + + if (!r.isNextNonSpace(')')) + throw new ParsingException("MODSEQ parse error"); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/MailboxInfo.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/MailboxInfo.java new file mode 100644 index 000000000..5f94fd31f --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/MailboxInfo.java @@ -0,0 +1,187 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.List; +import java.util.ArrayList; + +import javax.mail.Flags; + +import com.sun.mail.iap.*; + +/** + * Information collected when opening a mailbox. + * + * @author John Mani + * @author Bill Shannon + */ + +public class MailboxInfo { + /** The available flags. */ + public Flags availableFlags = null; + /** The permanent flags. */ + public Flags permanentFlags = null; + /** The total number of messages. */ + public int total = -1; + /** The number of recent messages. */ + public int recent = -1; + /** The first unseen message. */ + public int first = -1; + /** The UIDVALIDITY. */ + public long uidvalidity = -1; + /** The next UID value to be assigned. */ + public long uidnext = -1; + /** UIDs are not sticky. */ + public boolean uidNotSticky = false; // RFC 4315 + /** The highest MODSEQ value. */ + public long highestmodseq = -1; // RFC 4551 - CONDSTORE + /** Folder.READ_WRITE or Folder.READ_ONLY, set by IMAPProtocol. */ + public int mode; + /** VANISHED or FETCH responses received while opening the mailbox. */ + public List responses; + + /** + * Collect the information about this mailbox from the + * responses to a SELECT or EXAMINE. + * + * @param r the responses + * @exception ParsingException for errors parsing the responses + */ + public MailboxInfo(Response[] r) throws ParsingException { + for (int i = 0; i < r.length; i++) { + if (r[i] == null || !(r[i] instanceof IMAPResponse)) + continue; + + IMAPResponse ir = (IMAPResponse)r[i]; + + if (ir.keyEquals("EXISTS")) { + total = ir.getNumber(); + r[i] = null; // remove this response + } else if (ir.keyEquals("RECENT")) { + recent = ir.getNumber(); + r[i] = null; // remove this response + } else if (ir.keyEquals("FLAGS")) { + availableFlags = new FLAGS(ir); + r[i] = null; // remove this response + } else if (ir.keyEquals("VANISHED")) { + if (responses == null) + responses = new ArrayList<>(); + responses.add(ir); + r[i] = null; // remove this response + } else if (ir.keyEquals("FETCH")) { + if (responses == null) + responses = new ArrayList<>(); + responses.add(ir); + r[i] = null; // remove this response + } else if (ir.isUnTagged() && ir.isOK()) { + /* + * should be one of: + * * OK [UNSEEN 12] + * * OK [UIDVALIDITY 3857529045] + * * OK [PERMANENTFLAGS (\Deleted)] + * * OK [UIDNEXT 44] + * * OK [HIGHESTMODSEQ 103] + */ + ir.skipSpaces(); + + if (ir.readByte() != '[') { // huh ??? + ir.reset(); + continue; + } + + boolean handled = true; + String s = ir.readAtom(); + if (s.equalsIgnoreCase("UNSEEN")) + first = ir.readNumber(); + else if (s.equalsIgnoreCase("UIDVALIDITY")) + uidvalidity = ir.readLong(); + else if (s.equalsIgnoreCase("PERMANENTFLAGS")) + permanentFlags = new FLAGS(ir); + else if (s.equalsIgnoreCase("UIDNEXT")) + uidnext = ir.readLong(); + else if (s.equalsIgnoreCase("HIGHESTMODSEQ")) + highestmodseq = ir.readLong(); + else + handled = false; // possibly an ALERT + + if (handled) + r[i] = null; // remove this response + else + ir.reset(); // so ALERT can be read + } else if (ir.isUnTagged() && ir.isNO()) { + /* + * should be one of: + * * NO [UIDNOTSTICKY] + */ + ir.skipSpaces(); + + if (ir.readByte() != '[') { // huh ??? + ir.reset(); + continue; + } + + boolean handled = true; + String s = ir.readAtom(); + if (s.equalsIgnoreCase("UIDNOTSTICKY")) + uidNotSticky = true; + else + handled = false; // possibly an ALERT + + if (handled) + r[i] = null; // remove this response + else + ir.reset(); // so ALERT can be read + } + } + + /* + * The PERMANENTFLAGS response code is optional, and if + * not present implies that all flags in the required FLAGS + * response can be changed permanently. + */ + if (permanentFlags == null) { + if (availableFlags != null) + permanentFlags = new Flags(availableFlags); + else + permanentFlags = new Flags(); + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/MessageSet.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/MessageSet.java new file mode 100644 index 000000000..88671efe3 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/MessageSet.java @@ -0,0 +1,144 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.List; +import java.util.ArrayList; + +/** + * This class holds the 'start' and 'end' for a range of messages. + */ +public class MessageSet { + + public int start; + public int end; + + public MessageSet() { } + + public MessageSet(int start, int end) { + this.start = start; + this.end = end; + } + + /** + * Count the total number of elements in a MessageSet + * + * @return how many messages in this MessageSet + */ + public int size() { + return end - start + 1; + } + + /** + * Convert an array of integers into an array of MessageSets + * + * @param msgs the messages + * @return array of MessageSet objects + */ + public static MessageSet[] createMessageSets(int[] msgs) { + List v = new ArrayList<>(); + int i,j; + + for (i=0; i < msgs.length; i++) { + MessageSet ms = new MessageSet(); + ms.start = msgs[i]; + + // Look for contiguous elements + for (j=i+1; j < msgs.length; j++) { + if (msgs[j] != msgs[j-1] +1) + break; + } + ms.end = msgs[j-1]; + v.add(ms); + i = j-1; // i gets incremented @ top of the loop + } + return v.toArray(new MessageSet[v.size()]); + } + + /** + * Convert an array of MessageSets into an IMAP sequence range + * + * @param msgsets the MessageSets + * @return IMAP sequence string + */ + public static String toString(MessageSet[] msgsets) { + if (msgsets == null || msgsets.length == 0) // Empty msgset + return null; + + int i = 0; // msgset index + StringBuilder s = new StringBuilder(); + int size = msgsets.length; + int start, end; + + for (;;) { + start = msgsets[i].start; + end = msgsets[i].end; + + if (end > start) + s.append(start).append(':').append(end); + else // end == start means only one element + s.append(start); + + i++; // Next MessageSet + if (i >= size) // No more MessageSets + break; + else + s.append(','); + } + return s.toString(); + } + + + /* + * Count the total number of elements in an array of MessageSets + */ + public static int size(MessageSet[] msgsets) { + int count = 0; + + if (msgsets == null) // Null msgset + return 0; + + for (int i=0; i < msgsets.length; i++) + count += msgsets[i].size(); + + return count; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/Namespaces.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/Namespaces.java new file mode 100644 index 000000000..b9b3b854a --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/Namespaces.java @@ -0,0 +1,171 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.*; +import com.sun.mail.iap.*; + +/** + * This class and its inner class represent the response to the + * NAMESPACE command.

    + * + * See RFC 2342. + * + * @author Bill Shannon + */ + +public class Namespaces { + + /** + * A single namespace entry. + */ + public static class Namespace { + /** + * Prefix string for the namespace. + */ + public String prefix; + + /** + * Delimiter between names in this namespace. + */ + public char delimiter; + + /** + * Parse a namespace element out of the response. + * + * @param r the Response to parse + * @exception ProtocolException for any protocol errors + */ + public Namespace(Response r) throws ProtocolException { + // Namespace_Element = "(" string SP (<"> QUOTED_CHAR <"> / nil) + // *(Namespace_Response_Extension) ")" + if (!r.isNextNonSpace('(')) + throw new ProtocolException( + "Missing '(' at start of Namespace"); + // first, the prefix + prefix = r.readString(); + if (!r.supportsUtf8()) + prefix = BASE64MailboxDecoder.decode(prefix); + r.skipSpaces(); + // delimiter is a quoted character or NIL + if (r.peekByte() == '"') { + r.readByte(); + delimiter = (char)r.readByte(); + if (delimiter == '\\') + delimiter = (char)r.readByte(); + if (r.readByte() != '"') + throw new ProtocolException( + "Missing '\"' at end of QUOTED_CHAR"); + } else { + String s = r.readAtom(); + if (s == null) + throw new ProtocolException("Expected NIL, got null"); + if (!s.equalsIgnoreCase("NIL")) + throw new ProtocolException("Expected NIL, got " + s); + delimiter = 0; + } + // at end of Namespace data? + if (r.isNextNonSpace(')')) + return; + + // otherwise, must be a Namespace_Response_Extension + // Namespace_Response_Extension = SP string SP + // "(" string *(SP string) ")" + r.readString(); + r.skipSpaces(); + r.readStringList(); + if (!r.isNextNonSpace(')')) + throw new ProtocolException("Missing ')' at end of Namespace"); + } + }; + + /** + * The personal namespaces. + * May be null. + */ + public Namespace[] personal; + + /** + * The namespaces for other users. + * May be null. + */ + public Namespace[] otherUsers; + + /** + * The shared namespace. + * May be null. + */ + public Namespace[] shared; + + /** + * Parse out all the namespaces. + * + * @param r the Response to parse + * @throws ProtocolException for any protocol errors + */ + public Namespaces(Response r) throws ProtocolException { + personal = getNamespaces(r); + otherUsers = getNamespaces(r); + shared = getNamespaces(r); + } + + /** + * Parse out one of the three sets of namespaces. + */ + private Namespace[] getNamespaces(Response r) throws ProtocolException { + // Namespace = nil / "(" 1*( Namespace_Element) ")" + if (r.isNextNonSpace('(')) { + List v = new ArrayList<>(); + do { + Namespace ns = new Namespace(r); + v.add(ns); + } while (!r.isNextNonSpace(')')); + return v.toArray(new Namespace[v.size()]); + } else { + String s = r.readAtom(); + if (s == null) + throw new ProtocolException("Expected NIL, got null"); + if (!s.equalsIgnoreCase("NIL")) + throw new ProtocolException("Expected NIL, got " + s); + return null; + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/RFC822DATA.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/RFC822DATA.java new file mode 100644 index 000000000..3c440bd29 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/RFC822DATA.java @@ -0,0 +1,100 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.io.ByteArrayInputStream; +import com.sun.mail.iap.*; +import com.sun.mail.util.ASCIIUtility; + +/** + * The RFC822 response data item. + * + * @author John Mani + * @author Bill Shannon + */ + +public class RFC822DATA implements Item { + + static final char[] name = {'R','F','C','8','2','2'}; + private final int msgno; + private final ByteArray data; + private final boolean isHeader; + + /** + * Constructor, header flag is false. + * + * @param r the FetchResponse + * @exception ParsingException for parsing failures + */ + public RFC822DATA(FetchResponse r) throws ParsingException { + this(r, false); + } + + /** + * Constructor, specifying header flag. + * + * @param r the FetchResponse + * @param isHeader just header information? + * @exception ParsingException for parsing failures + */ + public RFC822DATA(FetchResponse r, boolean isHeader) + throws ParsingException { + this.isHeader = isHeader; + msgno = r.getNumber(); + r.skipSpaces(); + data = r.readByteArray(); + } + + public ByteArray getByteArray() { + return data; + } + + public ByteArrayInputStream getByteArrayInputStream() { + if (data != null) + return data.toByteArrayInputStream(); + else + return null; + } + + public boolean isHeader() { + return isHeader; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/RFC822SIZE.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/RFC822SIZE.java new file mode 100644 index 000000000..51fa06b2f --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/RFC822SIZE.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import com.sun.mail.iap.*; + +/** + * An RFC822SIZE FETCH item. + * + * @author John Mani + */ + +public class RFC822SIZE implements Item { + + static final char[] name = {'R','F','C','8','2','2','.','S','I','Z','E'}; + public int msgno; + + public long size; + + /** + * Constructor. + * + * @param r the FetchResponse + * @exception ParsingException for parsing failures + */ + public RFC822SIZE(FetchResponse r) throws ParsingException { + msgno = r.getNumber(); + r.skipSpaces(); + size = r.readLong(); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/SaslAuthenticator.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/SaslAuthenticator.java new file mode 100644 index 000000000..92f53bda1 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/SaslAuthenticator.java @@ -0,0 +1,53 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import com.sun.mail.iap.ProtocolException; + +/** + * Interface to make it easier to call IMAPSaslAuthenticator. + */ + +public interface SaslAuthenticator { + public boolean authenticate(String[] mechs, String realm, String authzid, + String u, String p) throws ProtocolException; + +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/SearchSequence.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/SearchSequence.java new file mode 100644 index 000000000..07e732179 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/SearchSequence.java @@ -0,0 +1,551 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.*; +import java.io.IOException; + +import javax.mail.*; +import javax.mail.search.*; +import com.sun.mail.iap.*; +import com.sun.mail.imap.OlderTerm; +import com.sun.mail.imap.YoungerTerm; +import com.sun.mail.imap.ModifiedSinceTerm; + +/** + * This class traverses a search-tree and generates the + * corresponding IMAP search sequence. + * + * Each IMAPProtocol instance contains an instance of this class, + * which might be subclassed by subclasses of IMAPProtocol to add + * support for additional product-specific search terms. + * + * @author John Mani + * @author Bill Shannon + */ +public class SearchSequence { + + private IMAPProtocol protocol; // for hasCapability checks; may be null + + /** + * Create a SearchSequence for this IMAPProtocol. + * + * @param p the IMAPProtocol object for the server + * @since JavaMail 1.6.0 + */ + public SearchSequence(IMAPProtocol p) { + protocol = p; + } + + /** + * Create a SearchSequence. + */ + @Deprecated + public SearchSequence() { + } + + /** + * Generate the IMAP search sequence for the given search expression. + * + * @param term the search term + * @param charset charset for the search + * @return the SEARCH Argument + * @exception SearchException for failures + * @exception IOException for I/O errors + */ + public Argument generateSequence(SearchTerm term, String charset) + throws SearchException, IOException { + /* + * Call the appropriate handler depending on the type of + * the search-term ... + */ + if (term instanceof AndTerm) // AND + return and((AndTerm)term, charset); + else if (term instanceof OrTerm) // OR + return or((OrTerm)term, charset); + else if (term instanceof NotTerm) // NOT + return not((NotTerm)term, charset); + else if (term instanceof HeaderTerm) // HEADER + return header((HeaderTerm)term, charset); + else if (term instanceof FlagTerm) // FLAG + return flag((FlagTerm)term); + else if (term instanceof FromTerm) { // FROM + FromTerm fterm = (FromTerm)term; + return from(fterm.getAddress().toString(), charset); + } + else if (term instanceof FromStringTerm) { // FROM + FromStringTerm fterm = (FromStringTerm)term; + return from(fterm.getPattern(), charset); + } + else if (term instanceof RecipientTerm) { // RECIPIENT + RecipientTerm rterm = (RecipientTerm)term; + return recipient(rterm.getRecipientType(), + rterm.getAddress().toString(), + charset); + } + else if (term instanceof RecipientStringTerm) { // RECIPIENT + RecipientStringTerm rterm = (RecipientStringTerm)term; + return recipient(rterm.getRecipientType(), + rterm.getPattern(), + charset); + } + else if (term instanceof SubjectTerm) // SUBJECT + return subject((SubjectTerm)term, charset); + else if (term instanceof BodyTerm) // BODY + return body((BodyTerm)term, charset); + else if (term instanceof SizeTerm) // SIZE + return size((SizeTerm)term); + else if (term instanceof SentDateTerm) // SENTDATE + return sentdate((SentDateTerm)term); + else if (term instanceof ReceivedDateTerm) // INTERNALDATE + return receiveddate((ReceivedDateTerm)term); + else if (term instanceof OlderTerm) // RFC 5032 OLDER + return older((OlderTerm)term); + else if (term instanceof YoungerTerm) // RFC 5032 YOUNGER + return younger((YoungerTerm)term); + else if (term instanceof MessageIDTerm) // MessageID + return messageid((MessageIDTerm)term, charset); + else if (term instanceof ModifiedSinceTerm) // RFC 4551 MODSEQ + return modifiedSince((ModifiedSinceTerm)term); + else + throw new SearchException("Search too complex"); + } + + /** + * Check if the "text" terms in the given SearchTerm contain + * non US-ASCII characters. + * + * @param term the search term + * @return true if only ASCII + */ + public static boolean isAscii(SearchTerm term) { + if (term instanceof AndTerm) + return isAscii(((AndTerm)term).getTerms()); + else if (term instanceof OrTerm) + return isAscii(((OrTerm)term).getTerms()); + else if (term instanceof NotTerm) + return isAscii(((NotTerm)term).getTerm()); + else if (term instanceof StringTerm) + return isAscii(((StringTerm)term).getPattern()); + else if (term instanceof AddressTerm) + return isAscii(((AddressTerm)term).getAddress().toString()); + + // Any other term returns true. + return true; + } + + /** + * Check if any of the "text" terms in the given SearchTerms contain + * non US-ASCII characters. + * + * @param terms the search terms + * @return true if only ASCII + */ + public static boolean isAscii(SearchTerm[] terms) { + for (int i = 0; i < terms.length; i++) + if (!isAscii(terms[i])) // outta here ! + return false; + return true; + } + + /** + * Does this string contain only ASCII characters? + * + * @param s the string + * @return true if only ASCII + */ + public static boolean isAscii(String s) { + int l = s.length(); + + for (int i=0; i < l; i++) { + if ((int)s.charAt(i) > 0177) // non-ascii + return false; + } + return true; + } + + protected Argument and(AndTerm term, String charset) + throws SearchException, IOException { + // Combine the sequences for both terms + SearchTerm[] terms = term.getTerms(); + // Generate the search sequence for the first term + Argument result = generateSequence(terms[0], charset); + // Append other terms + for (int i = 1; i < terms.length; i++) + result.append(generateSequence(terms[i], charset)); + return result; + } + + protected Argument or(OrTerm term, String charset) + throws SearchException, IOException { + SearchTerm[] terms = term.getTerms(); + + /* The IMAP OR operator takes only two operands. So if + * we have more than 2 operands, group them into 2-operand + * OR Terms. + */ + if (terms.length > 2) { + SearchTerm t = terms[0]; + + // Include rest of the terms + for (int i = 1; i < terms.length; i++) + t = new OrTerm(t, terms[i]); + + term = (OrTerm)t; // set 'term' to the new jumbo OrTerm we + // just created + terms = term.getTerms(); + } + + // 'term' now has only two operands + Argument result = new Argument(); + + // Add the OR search-key, if more than one term + if (terms.length > 1) + result.writeAtom("OR"); + + /* If this term is an AND expression, we need to enclose it + * within paranthesis. + * + * AND expressions are either AndTerms or FlagTerms + */ + if (terms[0] instanceof AndTerm || terms[0] instanceof FlagTerm) + result.writeArgument(generateSequence(terms[0], charset)); + else + result.append(generateSequence(terms[0], charset)); + + // Repeat the above for the second term, if there is one + if (terms.length > 1) { + if (terms[1] instanceof AndTerm || terms[1] instanceof FlagTerm) + result.writeArgument(generateSequence(terms[1], charset)); + else + result.append(generateSequence(terms[1], charset)); + } + + return result; + } + + protected Argument not(NotTerm term, String charset) + throws SearchException, IOException { + Argument result = new Argument(); + + // Add the NOT search-key + result.writeAtom("NOT"); + + /* If this term is an AND expression, we need to enclose it + * within paranthesis. + * + * AND expressions are either AndTerms or FlagTerms + */ + SearchTerm nterm = term.getTerm(); + if (nterm instanceof AndTerm || nterm instanceof FlagTerm) + result.writeArgument(generateSequence(nterm, charset)); + else + result.append(generateSequence(nterm, charset)); + + return result; + } + + protected Argument header(HeaderTerm term, String charset) + throws SearchException, IOException { + Argument result = new Argument(); + result.writeAtom("HEADER"); + result.writeString(term.getHeaderName()); + result.writeString(term.getPattern(), charset); + return result; + } + + protected Argument messageid(MessageIDTerm term, String charset) + throws SearchException, IOException { + Argument result = new Argument(); + result.writeAtom("HEADER"); + result.writeString("Message-ID"); + // XXX confirm that charset conversion ought to be done + result.writeString(term.getPattern(), charset); + return result; + } + + protected Argument flag(FlagTerm term) throws SearchException { + boolean set = term.getTestSet(); + + Argument result = new Argument(); + + Flags flags = term.getFlags(); + Flags.Flag[] sf = flags.getSystemFlags(); + String[] uf = flags.getUserFlags(); + if (sf.length == 0 && uf.length == 0) + throw new SearchException("Invalid FlagTerm"); + + for (int i = 0; i < sf.length; i++) { + if (sf[i] == Flags.Flag.DELETED) + result.writeAtom(set ? "DELETED": "UNDELETED"); + else if (sf[i] == Flags.Flag.ANSWERED) + result.writeAtom(set ? "ANSWERED": "UNANSWERED"); + else if (sf[i] == Flags.Flag.DRAFT) + result.writeAtom(set ? "DRAFT": "UNDRAFT"); + else if (sf[i] == Flags.Flag.FLAGGED) + result.writeAtom(set ? "FLAGGED": "UNFLAGGED"); + else if (sf[i] == Flags.Flag.RECENT) + result.writeAtom(set ? "RECENT": "OLD"); + else if (sf[i] == Flags.Flag.SEEN) + result.writeAtom(set ? "SEEN": "UNSEEN"); + } + + for (int i = 0; i < uf.length; i++) { + result.writeAtom(set ? "KEYWORD" : "UNKEYWORD"); + result.writeAtom(uf[i]); + } + + return result; + } + + protected Argument from(String address, String charset) + throws SearchException, IOException { + Argument result = new Argument(); + result.writeAtom("FROM"); + result.writeString(address, charset); + return result; + } + + protected Argument recipient(Message.RecipientType type, + String address, String charset) + throws SearchException, IOException { + Argument result = new Argument(); + + if (type == Message.RecipientType.TO) + result.writeAtom("TO"); + else if (type == Message.RecipientType.CC) + result.writeAtom("CC"); + else if (type == Message.RecipientType.BCC) + result.writeAtom("BCC"); + else + throw new SearchException("Illegal Recipient type"); + + result.writeString(address, charset); + return result; + } + + protected Argument subject(SubjectTerm term, String charset) + throws SearchException, IOException { + Argument result = new Argument(); + + result.writeAtom("SUBJECT"); + result.writeString(term.getPattern(), charset); + return result; + } + + protected Argument body(BodyTerm term, String charset) + throws SearchException, IOException { + Argument result = new Argument(); + + result.writeAtom("BODY"); + result.writeString(term.getPattern(), charset); + return result; + } + + protected Argument size(SizeTerm term) + throws SearchException { + Argument result = new Argument(); + + switch (term.getComparison()) { + case ComparisonTerm.GT: + result.writeAtom("LARGER"); + break; + case ComparisonTerm.LT: + result.writeAtom("SMALLER"); + break; + default: + // GT and LT is all we get from IMAP for size + throw new SearchException("Cannot handle Comparison"); + } + + result.writeNumber(term.getNumber()); + return result; + } + + // Date SEARCH stuff ... + + // NOTE: The built-in IMAP date comparisons are equivalent to + // "<" (BEFORE), "=" (ON), and ">=" (SINCE)!!! + // There is no built-in greater-than comparison! + + /** + * Print an IMAP Date string, that is suitable for the Date + * SEARCH commands. + * + * The IMAP Date string is : + * date ::= date_day "-" date_month "-" date_year + * + * Note that this format does not contain the TimeZone + */ + private static String monthTable[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + // A GregorianCalendar object in the current timezone + protected Calendar cal = new GregorianCalendar(); + + protected String toIMAPDate(Date date) { + StringBuilder s = new StringBuilder(); + + cal.setTime(date); + + s.append(cal.get(Calendar.DATE)).append("-"); + s.append(monthTable[cal.get(Calendar.MONTH)]).append('-'); + s.append(cal.get(Calendar.YEAR)); + + return s.toString(); + } + + protected Argument sentdate(DateTerm term) + throws SearchException { + Argument result = new Argument(); + String date = toIMAPDate(term.getDate()); + + switch (term.getComparison()) { + case ComparisonTerm.GT: + result.writeAtom("NOT SENTON " + date + " SENTSINCE " + date); + break; + case ComparisonTerm.EQ: + result.writeAtom("SENTON " + date); + break; + case ComparisonTerm.LT: + result.writeAtom("SENTBEFORE " + date); + break; + case ComparisonTerm.GE: + result.writeAtom("SENTSINCE " + date); + break; + case ComparisonTerm.LE: + result.writeAtom("OR SENTBEFORE " + date + " SENTON " + date); + break; + case ComparisonTerm.NE: + result.writeAtom("NOT SENTON " + date); + break; + default: + throw new SearchException("Cannot handle Date Comparison"); + } + + return result; + } + + protected Argument receiveddate(DateTerm term) + throws SearchException { + Argument result = new Argument(); + String date = toIMAPDate(term.getDate()); + + switch (term.getComparison()) { + case ComparisonTerm.GT: + result.writeAtom("NOT ON " + date + " SINCE " + date); + break; + case ComparisonTerm.EQ: + result.writeAtom("ON " + date); + break; + case ComparisonTerm.LT: + result.writeAtom("BEFORE " + date); + break; + case ComparisonTerm.GE: + result.writeAtom("SINCE " + date); + break; + case ComparisonTerm.LE: + result.writeAtom("OR BEFORE " + date + " ON " + date); + break; + case ComparisonTerm.NE: + result.writeAtom("NOT ON " + date); + break; + default: + throw new SearchException("Cannot handle Date Comparison"); + } + + return result; + } + + /** + * Generate argument for OlderTerm. + * + * @param term the search term + * @return the SEARCH Argument + * @exception SearchException for failures + * @since JavaMail 1.5.1 + */ + protected Argument older(OlderTerm term) throws SearchException { + if (protocol != null && !protocol.hasCapability("WITHIN")) + throw new SearchException("Server doesn't support OLDER searches"); + Argument result = new Argument(); + result.writeAtom("OLDER"); + result.writeNumber(term.getInterval()); + return result; + } + + /** + * Generate argument for YoungerTerm. + * + * @param term the search term + * @return the SEARCH Argument + * @exception SearchException for failures + * @since JavaMail 1.5.1 + */ + protected Argument younger(YoungerTerm term) throws SearchException { + if (protocol != null && !protocol.hasCapability("WITHIN")) + throw new SearchException("Server doesn't support YOUNGER searches"); + Argument result = new Argument(); + result.writeAtom("YOUNGER"); + result.writeNumber(term.getInterval()); + return result; + } + + /** + * Generate argument for ModifiedSinceTerm. + * + * @param term the search term + * @return the SEARCH Argument + * @exception SearchException for failures + * @since JavaMail 1.5.1 + */ + protected Argument modifiedSince(ModifiedSinceTerm term) + throws SearchException { + if (protocol != null && !protocol.hasCapability("CONDSTORE")) + throw new SearchException("Server doesn't support MODSEQ searches"); + Argument result = new Argument(); + result.writeAtom("MODSEQ"); + result.writeNumber(term.getModSeq()); + return result; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/Status.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/Status.java new file mode 100644 index 000000000..25600f4b0 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/Status.java @@ -0,0 +1,168 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.Map; +import java.util.HashMap; +import java.util.Locale; + +import com.sun.mail.iap.*; + +/** + * STATUS response. + * + * @author John Mani + * @author Bill Shannon + */ + +public class Status { + public String mbox = null; + public int total = -1; + public int recent = -1; + public long uidnext = -1; + public long uidvalidity = -1; + public int unseen = -1; + public long highestmodseq = -1; + public Map items; // any unknown items + + static final String[] standardItems = + { "MESSAGES", "RECENT", "UNSEEN", "UIDNEXT", "UIDVALIDITY" }; + + public Status(Response r) throws ParsingException { + // mailbox := astring + mbox = r.readAtomString(); + if (!r.supportsUtf8()) + mbox = BASE64MailboxDecoder.decode(mbox); + + // Workaround buggy IMAP servers that don't quote folder names + // with spaces. + final StringBuilder buffer = new StringBuilder(); + boolean onlySpaces = true; + + while (r.peekByte() != '(' && r.peekByte() != 0) { + final char next = (char)r.readByte(); + + buffer.append(next); + + if (next != ' ') { + onlySpaces = false; + } + } + + if (!onlySpaces) { + mbox = (mbox + buffer).trim(); + } + + if (r.readByte() != '(') + throw new ParsingException("parse error in STATUS"); + + do { + String attr = r.readAtom(); + if (attr == null) + throw new ParsingException("parse error in STATUS"); + if (attr.equalsIgnoreCase("MESSAGES")) + total = r.readNumber(); + else if (attr.equalsIgnoreCase("RECENT")) + recent = r.readNumber(); + else if (attr.equalsIgnoreCase("UIDNEXT")) + uidnext = r.readLong(); + else if (attr.equalsIgnoreCase("UIDVALIDITY")) + uidvalidity = r.readLong(); + else if (attr.equalsIgnoreCase("UNSEEN")) + unseen = r.readNumber(); + else if (attr.equalsIgnoreCase("HIGHESTMODSEQ")) + highestmodseq = r.readLong(); + else { + if (items == null) + items = new HashMap<>(); + items.put(attr.toUpperCase(Locale.ENGLISH), + Long.valueOf(r.readLong())); + } + } while (!r.isNextNonSpace(')')); + } + + /** + * Get the value for the STATUS item. + * + * @param item the STATUS item + * @return the value + * @since JavaMail 1.5.2 + */ + public long getItem(String item) { + item = item.toUpperCase(Locale.ENGLISH); + Long v; + long ret = -1; + if (items != null && (v = items.get(item)) != null) + ret = v.longValue(); + else if (item.equals("MESSAGES")) + ret = total; + else if (item.equals("RECENT")) + ret = recent; + else if (item.equals("UIDNEXT")) + ret = uidnext; + else if (item.equals("UIDVALIDITY")) + ret = uidvalidity; + else if (item.equals("UNSEEN")) + ret = unseen; + else if (item.equals("HIGHESTMODSEQ")) + ret = highestmodseq; + return ret; + } + + public static void add(Status s1, Status s2) { + if (s2.total != -1) + s1.total = s2.total; + if (s2.recent != -1) + s1.recent = s2.recent; + if (s2.uidnext != -1) + s1.uidnext = s2.uidnext; + if (s2.uidvalidity != -1) + s1.uidvalidity = s2.uidvalidity; + if (s2.unseen != -1) + s1.unseen = s2.unseen; + if (s2.highestmodseq != -1) + s1.highestmodseq = s2.highestmodseq; + if (s1.items == null) + s1.items = s2.items; + else if (s2.items != null) + s1.items.putAll(s2.items); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/UID.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/UID.java new file mode 100644 index 000000000..b917861bb --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/UID.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import com.sun.mail.iap.*; + +/** + * This class represents the UID data item. + * + * @author John Mani + */ + +public class UID implements Item { + + static final char[] name = {'U','I','D'}; + public int seqnum; + + public long uid; + + /** + * Constructor. + * + * @param r the FetchResponse + * @exception ParsingException for parsing failures + */ + public UID(FetchResponse r) throws ParsingException { + seqnum = r.getNumber(); + r.skipSpaces(); + uid = r.readLong(); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/UIDSet.java b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/UIDSet.java new file mode 100644 index 000000000..f6fdebdbf --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/UIDSet.java @@ -0,0 +1,259 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.imap.protocol; + +import java.util.List; +import java.util.ArrayList; +import java.util.StringTokenizer; + +/** + * This class holds the 'start' and 'end' for a range of UIDs. + * Just like MessageSet except using long instead of int. + */ +public class UIDSet { + + public long start; + public long end; + + public UIDSet() { } + + public UIDSet(long start, long end) { + this.start = start; + this.end = end; + } + + /** + * Count the total number of elements in a UIDSet + * + * @return the number of elements + */ + public long size() { + return end - start + 1; + } + + /** + * Convert an array of longs into an array of UIDSets + * + * @param uids the UIDs + * @return array of UIDSet objects + */ + public static UIDSet[] createUIDSets(long[] uids) { + if (uids == null) + return null; + List v = new ArrayList<>(); + int i,j; + + for (i=0; i < uids.length; i++) { + UIDSet ms = new UIDSet(); + ms.start = uids[i]; + + // Look for contiguous elements + for (j=i+1; j < uids.length; j++) { + if (uids[j] != uids[j-1] +1) + break; + } + ms.end = uids[j-1]; + v.add(ms); + i = j-1; // i gets incremented @ top of the loop + } + UIDSet[] uidset = new UIDSet[v.size()]; + return v.toArray(uidset); + } + + /** + * Parse a string in IMAP UID range format. + * + * @param uids UID string + * @return array of UIDSet objects + * @since JavaMail 1.5.1 + */ + public static UIDSet[] parseUIDSets(String uids) { + if (uids == null) + return null; + List v = new ArrayList<>(); + StringTokenizer st = new StringTokenizer(uids, ",:", true); + long start = -1; + UIDSet cur = null; + try { + while(st.hasMoreTokens()) { + String s = st.nextToken(); + if (s.equals(",")) { + if (cur != null) + v.add(cur); + cur = null; + } else if (s.equals(":")) { + // nothing to do, wait for next number + } else { // better be a number + long n = Long.parseLong(s); + if (cur != null) + cur.end = n; + else + cur = new UIDSet(n, n); + } + } + } catch (NumberFormatException nex) { + // give up and return what we have so far + } + if (cur != null) + v.add(cur); + UIDSet[] uidset = new UIDSet[v.size()]; + return v.toArray(uidset); + } + + /** + * Convert an array of UIDSets into an IMAP sequence range. + * + * @param uidset the UIDSets + * @return the IMAP sequence string + */ + public static String toString(UIDSet[] uidset) { + if (uidset == null) + return null; + if (uidset.length == 0) // Empty uidset + return ""; + + int i = 0; // uidset index + StringBuilder s = new StringBuilder(); + int size = uidset.length; + long start, end; + + for (;;) { + start = uidset[i].start; + end = uidset[i].end; + + if (end > start) + s.append(start).append(':').append(end); + else // end == start means only one element + s.append(start); + + i++; // Next UIDSet + if (i >= size) // No more UIDSets + break; + else + s.append(','); + } + return s.toString(); + } + + /** + * Convert an array of UIDSets into a array of long UIDs. + * + * @param uidset the UIDSets + * @return arrray of UIDs + * @since JavaMail 1.5.1 + */ + public static long[] toArray(UIDSet[] uidset) { + //return toArray(uidset, -1); + if (uidset == null) + return null; + long[] uids = new long[(int)UIDSet.size(uidset)]; + int i = 0; + for (UIDSet u : uidset) { + for (long n = u.start; n <= u.end; n++) + uids[i++] = n; + } + return uids; + } + + /** + * Convert an array of UIDSets into a array of long UIDs. + * Don't include any UIDs larger than uidmax. + * + * @param uidset the UIDSets + * @param uidmax maximum UID + * @return arrray of UIDs + * @since JavaMail 1.5.1 + */ + public static long[] toArray(UIDSet[] uidset, long uidmax) { + if (uidset == null) + return null; + long[] uids = new long[(int)UIDSet.size(uidset, uidmax)]; + int i = 0; + for (UIDSet u : uidset) { + for (long n = u.start; n <= u.end; n++) { + if (uidmax >= 0 && n > uidmax) + break; + uids[i++] = n; + } + } + return uids; + } + + /** + * Count the total number of elements in an array of UIDSets. + * + * @param uidset the UIDSets + * @return the number of elements + */ + public static long size(UIDSet[] uidset) { + long count = 0; + + if (uidset != null) + for (UIDSet u : uidset) + count += u.size(); + + return count; + } + + /** + * Count the total number of elements in an array of UIDSets. + * Don't count UIDs greater then uidmax. + * + * @since JavaMail 1.5.1 + */ + private static long size(UIDSet[] uidset, long uidmax) { + long count = 0; + + if (uidset != null) + for (UIDSet u : uidset) { + if (uidmax < 0) + count += u.size(); + else if (u.start <= uidmax) { + if (u.end < uidmax) + count += u.end - u.start + 1; + else + count += uidmax - u.start + 1; + } + } + + return count; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/package.html b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/package.html new file mode 100644 index 000000000..43c10eecd --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/imap/protocol/package.html @@ -0,0 +1,57 @@ + + + + + + +com.sun.mail.imap.protocol package + + + +

    +This package includes internal IMAP support classes and +SHOULD NOT BE USED DIRECTLY BY APPLICATIONS. +

    + + + diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/AppendStream.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/AppendStream.java new file mode 100644 index 000000000..9ef6c43ba --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/AppendStream.java @@ -0,0 +1,91 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; + +/** + * A stream for writing to the temp file, and when done can return a stream for + * reading the data just written. NOTE: We assume that only one thread is + * writing to the file at a time. + */ +class AppendStream extends OutputStream { + + private final WritableSharedFile tf; + private RandomAccessFile raf; + private final long start; + private long end; + + public AppendStream(WritableSharedFile tf) throws IOException { + this.tf = tf; + raf = tf.getWritableFile(); + start = raf.length(); + raf.seek(start); + } + + @Override + public void write(int b) throws IOException { + raf.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + raf.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + raf.write(b, off, len); + } + + @Override + public synchronized void close() throws IOException { + end = tf.updateLength(); + raf = null; // no more writing allowed + } + + public synchronized InputStream getInputStream() throws IOException { + return tf.newStream(start, end); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/DefaultFolder.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/DefaultFolder.java new file mode 100644 index 000000000..4ceb36e43 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/DefaultFolder.java @@ -0,0 +1,165 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import javax.mail.*; + +/** + * The POP3 DefaultFolder. Only contains the "INBOX" folder. + * + * @author Christopher Cotton + */ +public class DefaultFolder extends Folder { + + DefaultFolder(POP3Store store) { + super(store); + } + + @Override + public String getName() { + return ""; + } + + @Override + public String getFullName() { + return ""; + } + + @Override + public Folder getParent() { + return null; + } + + @Override + public boolean exists() { + return true; + } + + @Override + public Folder[] list(String pattern) throws MessagingException { + Folder[] f = { getInbox() }; + return f; + } + + @Override + public char getSeparator() { + return '/'; + } + + @Override + public int getType() { + return HOLDS_FOLDERS; + } + + @Override + public boolean create(int type) throws MessagingException { + return false; + } + + @Override + public boolean hasNewMessages() throws MessagingException { + return false; + } + + @Override + public Folder getFolder(String name) throws MessagingException { + if (!name.equalsIgnoreCase("INBOX")) { + throw new MessagingException("only INBOX supported"); + } else { + return getInbox(); + } + } + + protected Folder getInbox() throws MessagingException { + return getStore().getFolder("INBOX"); + } + + + @Override + public boolean delete(boolean recurse) throws MessagingException { + throw new MethodNotSupportedException("delete"); + } + + @Override + public boolean renameTo(Folder f) throws MessagingException { + throw new MethodNotSupportedException("renameTo"); + } + + @Override + public void open(int mode) throws MessagingException { + throw new MethodNotSupportedException("open"); + } + + @Override + public void close(boolean expunge) throws MessagingException { + throw new MethodNotSupportedException("close"); + } + + @Override + public boolean isOpen() { + return false; + } + + @Override + public Flags getPermanentFlags() { + return new Flags(); // empty flags object + } + + @Override + public int getMessageCount() throws MessagingException { + return 0; + } + + @Override + public Message getMessage(int msgno) throws MessagingException { + throw new MethodNotSupportedException("getMessage"); + } + + @Override + public void appendMessages(Message[] msgs) throws MessagingException { + throw new MethodNotSupportedException("Append not supported"); + } + + @Override + public Message[] expunge() throws MessagingException { + throw new MethodNotSupportedException("expunge"); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Folder.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Folder.java new file mode 100644 index 000000000..2589cab37 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Folder.java @@ -0,0 +1,635 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import javax.mail.*; +import javax.mail.event.*; +import java.io.InputStream; +import java.io.IOException; +import java.io.EOFException; +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.lang.reflect.Constructor; + +import com.sun.mail.util.LineInputStream; +import com.sun.mail.util.MailLogger; +import java.util.ArrayList; +import java.util.List; + +/** + * A POP3 Folder (can only be "INBOX"). + * + * See the com.sun.mail.pop3 package + * documentation for further information on the POP3 protocol provider.

    + * + * @author Bill Shannon + * @author John Mani (ported to the javax.mail APIs) + */ +public class POP3Folder extends Folder { + + private String name; + private POP3Store store; + private volatile Protocol port; + private int total; + private int size; + private boolean exists = false; + private volatile boolean opened = false; + private POP3Message[] message_cache; + private boolean doneUidl = false; + private volatile TempFile fileCache = null; + private boolean forceClose; + + MailLogger logger; // package private, for POP3Message + + protected POP3Folder(POP3Store store, String name) { + super(store); + this.name = name; + this.store = store; + if (name.equalsIgnoreCase("INBOX")) + exists = true; + logger = new MailLogger(this.getClass(), "DEBUG POP3", + store.getSession().getDebug(), store.getSession().getDebugOut()); + } + + @Override + public String getName() { + return name; + } + + @Override + public String getFullName() { + return name; + } + + @Override + public Folder getParent() { + return new DefaultFolder(store); + } + + /** + * Always true for the folder "INBOX", always false for + * any other name. + * + * @return true for INBOX, false otherwise + */ + @Override + public boolean exists() { + return exists; + } + + /** + * Always throws MessagingException because no POP3 folders + * can contain subfolders. + * + * @exception MessagingException always + */ + @Override + public Folder[] list(String pattern) throws MessagingException { + throw new MessagingException("not a directory"); + } + + /** + * Always returns a NUL character because POP3 doesn't support a hierarchy. + * + * @return NUL + */ + @Override + public char getSeparator() { + return '\0'; + } + + /** + * Always returns Folder.HOLDS_MESSAGES. + * + * @return Folder.HOLDS_MESSAGES + */ + @Override + public int getType() { + return HOLDS_MESSAGES; + } + + /** + * Always returns false; the POP3 protocol doesn't + * support creating folders. + * + * @return false + */ + @Override + public boolean create(int type) throws MessagingException { + return false; + } + + /** + * Always returns false; the POP3 protocol provides + * no way to determine when a new message arrives. + * + * @return false + */ + @Override + public boolean hasNewMessages() throws MessagingException { + return false; // no way to know + } + + /** + * Always throws MessagingException because no POP3 folders + * can contain subfolders. + * + * @exception MessagingException always + */ + @Override + public Folder getFolder(String name) throws MessagingException { + throw new MessagingException("not a directory"); + } + + /** + * Always throws MethodNotSupportedException + * because the POP3 protocol doesn't allow the INBOX to + * be deleted. + * + * @exception MethodNotSupportedException always + */ + @Override + public boolean delete(boolean recurse) throws MessagingException { + throw new MethodNotSupportedException("delete"); + } + + /** + * Always throws MethodNotSupportedException + * because the POP3 protocol doesn't support multiple folders. + * + * @exception MethodNotSupportedException always + */ + @Override + public boolean renameTo(Folder f) throws MessagingException { + throw new MethodNotSupportedException("renameTo"); + } + + /** + * Throws FolderNotFoundException unless this + * folder is named "INBOX". + * + * @exception FolderNotFoundException if not INBOX + * @exception AuthenticationFailedException authentication failures + * @exception MessagingException other open failures + */ + @Override + public synchronized void open(int mode) throws MessagingException { + checkClosed(); + if (!exists) + throw new FolderNotFoundException(this, "folder is not INBOX"); + + try { + port = store.getPort(this); + Status s = port.stat(); + total = s.total; + size = s.size; + this.mode = mode; + if (store.useFileCache) { + try { + fileCache = new TempFile(store.fileCacheDir); + } catch (IOException ex) { + logger.log(Level.FINE, "failed to create file cache", ex); + throw ex; // caught below + } + } + opened = true; + } catch (IOException ioex) { + try { + if (port != null) + port.quit(); + } catch (IOException ioex2) { + // ignore + } finally { + port = null; + store.closePort(this); + } + throw new MessagingException("Open failed", ioex); + } + + // Create the message cache array of appropriate size + message_cache = new POP3Message[total]; + doneUidl = false; + + notifyConnectionListeners(ConnectionEvent.OPENED); + } + + @Override + public synchronized void close(boolean expunge) throws MessagingException { + checkOpen(); + + try { + /* + * Some POP3 servers will mark messages for deletion when + * they're read. To prevent such messages from being + * deleted before the client deletes them, you can set + * the mail.pop3.rsetbeforequit property to true. This + * causes us to issue a POP3 RSET command to clear all + * the "marked for deletion" flags. We can then explicitly + * delete messages as desired. + */ + if (store.rsetBeforeQuit && !forceClose) + port.rset(); + POP3Message m; + if (expunge && mode == READ_WRITE && !forceClose) { + // find all messages marked deleted and issue DELE commands + for (int i = 0; i < message_cache.length; i++) { + if ((m = message_cache[i]) != null) { + if (m.isSet(Flags.Flag.DELETED)) + try { + port.dele(i + 1); + } catch (IOException ioex) { + throw new MessagingException( + "Exception deleting messages during close", + ioex); + } + } + } + } + + /* + * Flush and free all cached data for the messages. + */ + for (int i = 0; i < message_cache.length; i++) { + if ((m = message_cache[i]) != null) + m.invalidate(true); + } + + if (forceClose) + port.close(); + else + port.quit(); + } catch (IOException ex) { + // do nothing + } finally { + port = null; + store.closePort(this); + message_cache = null; + opened = false; + notifyConnectionListeners(ConnectionEvent.CLOSED); + if (fileCache != null) { + fileCache.close(); + fileCache = null; + } + } + } + + @Override + public synchronized boolean isOpen() { + if (!opened) + return false; + try { + if (!port.noop()) + throw new IOException("NOOP failed"); + } catch (IOException ioex) { + try { + close(false); + } catch (MessagingException mex) { + // ignore it + } + return false; + } + return true; + } + + /** + * Always returns an empty Flags object because + * the POP3 protocol doesn't support any permanent flags. + * + * @return empty Flags object + */ + @Override + public Flags getPermanentFlags() { + return new Flags(); // empty flags object + } + + /** + * Will not change while the folder is open because the POP3 + * protocol doesn't support notification of new messages + * arriving in open folders. + */ + @Override + public synchronized int getMessageCount() throws MessagingException { + if (!opened) + return -1; + checkReadable(); + return total; + } + + @Override + public synchronized Message getMessage(int msgno) + throws MessagingException { + checkOpen(); + + POP3Message m; + + // Assuming that msgno is <= total + if ((m = message_cache[msgno-1]) == null) { + m = createMessage(this, msgno); + message_cache[msgno-1] = m; + } + return m; + } + + protected POP3Message createMessage(Folder f, int msgno) + throws MessagingException { + POP3Message m = null; + Constructor cons = store.messageConstructor; + if (cons != null) { + try { + Object[] o = { this, Integer.valueOf(msgno) }; + m = (POP3Message)cons.newInstance(o); + } catch (Exception ex) { + // ignore + } + } + if (m == null) + m = new POP3Message(this, msgno); + return m; + } + + /** + * Always throws MethodNotSupportedException + * because the POP3 protocol doesn't support appending messages. + * + * @exception MethodNotSupportedException always + */ + @Override + public void appendMessages(Message[] msgs) throws MessagingException { + throw new MethodNotSupportedException("Append not supported"); + } + + /** + * Always throws MethodNotSupportedException + * because the POP3 protocol doesn't support expunging messages + * without closing the folder; call the {@link #close close} method + * with the expunge argument set to true + * instead. + * + * @exception MethodNotSupportedException always + */ + @Override + public Message[] expunge() throws MessagingException { + throw new MethodNotSupportedException("Expunge not supported"); + } + + /** + * Prefetch information about POP3 messages. + * If the FetchProfile contains UIDFolder.FetchProfileItem.UID, + * POP3 UIDs for all messages in the folder are fetched using the POP3 + * UIDL command. + * If the FetchProfile contains FetchProfile.Item.ENVELOPE, + * the headers and size of all messages are fetched using the POP3 TOP + * and LIST commands. + */ + @Override + public synchronized void fetch(Message[] msgs, FetchProfile fp) + throws MessagingException { + checkReadable(); + if (!doneUidl && store.supportsUidl && + fp.contains(UIDFolder.FetchProfileItem.UID)) { + /* + * Since the POP3 protocol only lets us fetch the UID + * for a single message or for all messages, we go ahead + * and fetch UIDs for all messages here, ignoring the msgs + * parameter. We could be more intelligent and base this + * decision on the number of messages fetched, or the + * percentage of the total number of messages fetched. + */ + String[] uids = new String[message_cache.length]; + try { + if (!port.uidl(uids)) + return; + } catch (EOFException eex) { + close(false); + throw new FolderClosedException(this, eex.toString()); + } catch (IOException ex) { + throw new MessagingException("error getting UIDL", ex); + } + for (int i = 0; i < uids.length; i++) { + if (uids[i] == null) + continue; + POP3Message m = (POP3Message)getMessage(i + 1); + m.uid = uids[i]; + } + doneUidl = true; // only do this once + } + if (fp.contains(FetchProfile.Item.ENVELOPE)) { + for (int i = 0; i < msgs.length; i++) { + try { + POP3Message msg = (POP3Message)msgs[i]; + // fetch headers + msg.getHeader(""); + // fetch message size + msg.getSize(); + } catch (MessageRemovedException mex) { + // should never happen, but ignore it if it does + } + } + } + } + + /** + * Return the unique ID string for this message, or null if + * not available. Uses the POP3 UIDL command. + * + * @param msg the message + * @return unique ID string + * @exception MessagingException for failures + */ + public synchronized String getUID(Message msg) throws MessagingException { + checkOpen(); + if (!(msg instanceof POP3Message)) + throw new MessagingException("message is not a POP3Message"); + POP3Message m = (POP3Message)msg; + try { + if (!store.supportsUidl) + return null; + if (m.uid == POP3Message.UNKNOWN) + m.uid = port.uidl(m.getMessageNumber()); + return m.uid; + } catch (EOFException eex) { + close(false); + throw new FolderClosedException(this, eex.toString()); + } catch (IOException ex) { + throw new MessagingException("error getting UIDL", ex); + } + } + + /** + * Return the size of this folder, as was returned by the POP3 STAT + * command when this folder was opened. + * + * @return folder size + * @exception IllegalStateException if the folder isn't open + * @exception MessagingException for other failures + */ + public synchronized int getSize() throws MessagingException { + checkOpen(); + return size; + } + + /** + * Return the sizes of all messages in this folder, as returned + * by the POP3 LIST command. Each entry in the array corresponds + * to a message; entry i corresponds to message number i+1. + * + * @return array of message sizes + * @exception IllegalStateException if the folder isn't open + * @exception MessagingException for other failures + * @since JavaMail 1.3.3 + */ + public synchronized int[] getSizes() throws MessagingException { + checkOpen(); + int sizes[] = new int[total]; + InputStream is = null; + LineInputStream lis = null; + try { + is = port.list(); + lis = new LineInputStream(is); + String line; + while ((line = lis.readLine()) != null) { + try { + StringTokenizer st = new StringTokenizer(line); + int msgnum = Integer.parseInt(st.nextToken()); + int size = Integer.parseInt(st.nextToken()); + if (msgnum > 0 && msgnum <= total) + sizes[msgnum - 1] = size; + } catch (RuntimeException e) { + } + } + } catch (IOException ex) { + // ignore it? + } finally { + try { + if (lis != null) + lis.close(); + } catch (IOException cex) { } + try { + if (is != null) + is.close(); + } catch (IOException cex) { } + } + return sizes; + } + + /** + * Return the raw results of the POP3 LIST command with no arguments. + * + * @return InputStream containing results + * @exception IllegalStateException if the folder isn't open + * @exception IOException for I/O errors talking to the server + * @exception MessagingException for other errors + * @since JavaMail 1.3.3 + */ + public synchronized InputStream listCommand() + throws MessagingException, IOException { + checkOpen(); + return port.list(); + } + + /** + * Close the folder when we're finalized. + */ + @Override + protected void finalize() throws Throwable { + forceClose = !store.finalizeCleanClose; + try { + if (opened) + close(false); + } finally { + super.finalize(); + forceClose = false; + } + } + + /* Ensure the folder is open */ + private void checkOpen() throws IllegalStateException { + if (!opened) + throw new IllegalStateException("Folder is not Open"); + } + + /* Ensure the folder is not open */ + private void checkClosed() throws IllegalStateException { + if (opened) + throw new IllegalStateException("Folder is Open"); + } + + /* Ensure the folder is open & readable */ + private void checkReadable() throws IllegalStateException { + if (!opened || (mode != READ_ONLY && mode != READ_WRITE)) + throw new IllegalStateException("Folder is not Readable"); + } + + /* Ensure the folder is open & writable */ + /* + private void checkWritable() throws IllegalStateException { + if (!opened || mode != READ_WRITE) + throw new IllegalStateException("Folder is not Writable"); + } + */ + + /** + * Centralize access to the Protocol object by POP3Message + * objects so that they will fail appropriately when the folder + * is closed. + */ + Protocol getProtocol() throws MessagingException { + Protocol p = port; // read it before close() can set it to null + checkOpen(); + // close() might happen here + return p; + } + + /* + * Only here to make accessible to POP3Message. + */ + @Override + protected void notifyMessageChangedListeners(int type, Message m) { + super.notifyMessageChangedListeners(type, m); + } + + /** + * Used by POP3Message. + */ + TempFile getFileCache() { + return fileCache; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Message.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Message.java new file mode 100644 index 000000000..fc1c5f041 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Message.java @@ -0,0 +1,668 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import java.io.*; +import java.util.Enumeration; +import java.util.logging.Level; +import java.lang.ref.SoftReference; +import javax.mail.*; +import javax.mail.internet.*; +import javax.mail.event.*; +import com.sun.mail.util.ReadableMime; + +/** + * A POP3 Message. Just like a MimeMessage except that + * some things are not supported. + * + * @author Bill Shannon + */ +public class POP3Message extends MimeMessage implements ReadableMime { + + /* + * Our locking strategy is to always lock the POP3Folder before the + * POP3Message so we have to be careful to drop our lock before calling + * back to the folder to close it and notify of connection lost events. + */ + + // flag to indicate we haven't tried to fetch the UID yet + static final String UNKNOWN = "UNKNOWN"; + + private POP3Folder folder; // overrides folder in MimeMessage + private int hdrSize = -1; + private int msgSize = -1; + String uid = UNKNOWN; // controlled by folder lock + + // rawData itself is never null + private SoftReference rawData + = new SoftReference<>(null); + + public POP3Message(Folder folder, int msgno) + throws MessagingException { + super(folder, msgno); + assert folder instanceof POP3Folder; + this.folder = (POP3Folder)folder; + } + + /** + * Set the specified flags on this message to the specified value. + * + * @param newFlags the flags to be set + * @param set the value to be set + */ + @Override + public synchronized void setFlags(Flags newFlags, boolean set) + throws MessagingException { + Flags oldFlags = (Flags)flags.clone(); + super.setFlags(newFlags, set); + if (!flags.equals(oldFlags)) + folder.notifyMessageChangedListeners( + MessageChangedEvent.FLAGS_CHANGED, this); + } + + /** + * Return the size of the content of this message in bytes. + * Returns -1 if the size cannot be determined.

    + * + * Note that this number may not be an exact measure of the + * content size and may or may not account for any transfer + * encoding of the content.

    + * + * @return size of content in bytes + * @exception MessagingException for failures + */ + @Override + public int getSize() throws MessagingException { + try { + synchronized (this) { + // if we already have the size, return it + if (msgSize > 0) + return msgSize; + } + + /* + * Use LIST to determine the entire message + * size and subtract out the header size + * (which may involve loading the headers, + * which may load the content as a side effect). + * If the content is loaded as a side effect of + * loading the headers, it will set the size. + * + * Make sure to call loadHeaders() outside of the + * synchronization block. There's a potential race + * condition here but synchronization will occur in + * loadHeaders() to make sure the headers are only + * loaded once, and again in the following block to + * only compute msgSize once. + */ + if (headers == null) + loadHeaders(); + + synchronized (this) { + if (msgSize < 0) + msgSize = folder.getProtocol().list(msgnum) - hdrSize; + return msgSize; + } + } catch (EOFException eex) { + folder.close(false); + throw new FolderClosedException(folder, eex.toString()); + } catch (IOException ex) { + throw new MessagingException("error getting size", ex); + } + } + + /** + * Produce the raw bytes of the message. The data is fetched using + * the POP3 RETR command. If skipHeader is true, just the content + * is returned. + */ + private InputStream getRawStream(boolean skipHeader) + throws MessagingException { + InputStream rawcontent = null; + try { + synchronized(this) { + rawcontent = rawData.get(); + if (rawcontent == null) { + TempFile cache = folder.getFileCache(); + if (cache != null) { + if (folder.logger.isLoggable(Level.FINE)) + folder.logger.fine("caching message #" + msgnum + + " in temp file"); + AppendStream os = cache.getAppendStream(); + BufferedOutputStream bos = new BufferedOutputStream(os); + try { + folder.getProtocol().retr(msgnum, bos); + } finally { + bos.close(); + } + rawcontent = os.getInputStream(); + } else { + rawcontent = folder.getProtocol().retr(msgnum, + msgSize > 0 ? msgSize + hdrSize : 0); + } + if (rawcontent == null) { + expunged = true; + throw new MessageRemovedException( + "can't retrieve message #" + msgnum + + " in POP3Message.getContentStream"); // XXX - what else? + } + + if (headers == null || + ((POP3Store)(folder.getStore())).forgetTopHeaders) { + headers = new InternetHeaders(rawcontent); + hdrSize = + (int)((SharedInputStream)rawcontent).getPosition(); + } else { + /* + * Already have the headers, have to skip the headers + * in the content array and return the body. + * + * XXX - It seems that some mail servers return slightly + * different headers in the RETR results than were returned + * in the TOP results, so we can't depend on remembering + * the size of the headers from the TOP command and just + * skipping that many bytes. Instead, we have to process + * the content, skipping over the header until we come to + * the empty line that separates the header from the body. + */ + int offset = 0; + for (;;) { + int len = 0; // number of bytes in this line + int c1; + while ((c1 = rawcontent.read()) >= 0) { + if (c1 == '\n') // end of line + break; + else if (c1 == '\r') { + // got CR, is the next char LF? + if (rawcontent.available() > 0) { + rawcontent.mark(1); + if (rawcontent.read() != '\n') + rawcontent.reset(); + } + break; // in any case, end of line + } + + // not CR, NL, or CRLF, count the byte + len++; + } + // here when end of line or out of data + + // if out of data, we're done + if (rawcontent.available() == 0) + break; + + // if it was an empty line, we're done + if (len == 0) + break; + } + hdrSize = + (int)((SharedInputStream)rawcontent).getPosition(); + } + + // skipped the header, the message is what's left + msgSize = rawcontent.available(); + + rawData = new SoftReference<>(rawcontent); + } + } + } catch (EOFException eex) { + folder.close(false); + throw new FolderClosedException(folder, eex.toString()); + } catch (IOException ex) { + throw new MessagingException("error fetching POP3 content", ex); + } + + /* + * We have a cached stream, but we need to return + * a fresh stream to read from the beginning and + * that can be safely closed. + */ + rawcontent = ((SharedInputStream)rawcontent).newStream( + skipHeader ? hdrSize : 0, -1); + return rawcontent; + } + + /** + * Produce the raw bytes of the content. The data is fetched using + * the POP3 RETR command. + * + * @see #contentStream + */ + @Override + protected synchronized InputStream getContentStream() + throws MessagingException { + if (contentStream != null) + return ((SharedInputStream)contentStream).newStream(0, -1); + + InputStream cstream = getRawStream(true); + + /* + * Keep a hard reference to the data if we're using a file + * cache or if the "mail.pop3.keepmessagecontent" prop is set. + */ + TempFile cache = folder.getFileCache(); + if (cache != null || + ((POP3Store)(folder.getStore())).keepMessageContent) + contentStream = ((SharedInputStream)cstream).newStream(0, -1); + return cstream; + } + + /** + * Return the MIME format stream corresponding to this message part. + * + * @return the MIME format stream + * @since JavaMail 1.4.5 + */ + @Override + public InputStream getMimeStream() throws MessagingException { + return getRawStream(false); + } + + /** + * Invalidate the cache of content for this message object, causing + * it to be fetched again from the server the next time it is needed. + * If invalidateHeaders is true, invalidate the headers + * as well. + * + * @param invalidateHeaders invalidate the headers as well? + */ + public synchronized void invalidate(boolean invalidateHeaders) { + content = null; + InputStream rstream = rawData.get(); + if (rstream != null) { + // note that if the content is in the file cache, it will be lost + // and fetched from the server if it's needed again + try { + rstream.close(); + } catch (IOException ex) { + // ignore it + } + rawData = new SoftReference<>(null); + } + if (contentStream != null) { + try { + contentStream.close(); + } catch (IOException ex) { + // ignore it + } + contentStream = null; + } + msgSize = -1; + if (invalidateHeaders) { + headers = null; + hdrSize = -1; + } + } + + /** + * Fetch the header of the message and the first n lines + * of the raw content of the message. The headers and data are + * available in the returned InputStream. + * + * @param n number of lines of content to fetch + * @return InputStream containing the message headers and n content lines + * @exception MessagingException for failures + */ + public InputStream top(int n) throws MessagingException { + try { + synchronized (this) { + return folder.getProtocol().top(msgnum, n); + } + } catch (EOFException eex) { + folder.close(false); + throw new FolderClosedException(folder, eex.toString()); + } catch (IOException ex) { + throw new MessagingException("error getting size", ex); + } + } + + /** + * Get all the headers for this header_name. Note that certain + * headers may be encoded as per RFC 2047 if they contain + * non US-ASCII characters and these should be decoded.

    + * + * @param name name of header + * @return array of headers + * @exception MessagingException for failures + * @see javax.mail.internet.MimeUtility + */ + @Override + public String[] getHeader(String name) + throws MessagingException { + if (headers == null) + loadHeaders(); + return headers.getHeader(name); + } + + /** + * Get all the headers for this header name, returned as a single + * String, with headers separated by the delimiter. If the + * delimiter is null, only the first header is + * returned. + * + * @param name the name of this header + * @param delimiter delimiter between returned headers + * @return the value fields for all headers with + * this name + * @exception MessagingException for failures + */ + @Override + public String getHeader(String name, String delimiter) + throws MessagingException { + if (headers == null) + loadHeaders(); + return headers.getHeader(name, delimiter); + } + + /** + * Set the value for this header_name. Throws IllegalWriteException + * because POP3 messages are read-only. + * + * @param name header name + * @param value header value + * @see javax.mail.internet.MimeUtility + * @exception IllegalWriteException because the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setHeader(String name, String value) + throws MessagingException { + // XXX - should check for read-only folder? + throw new IllegalWriteException("POP3 messages are read-only"); + } + + /** + * Add this value to the existing values for this header_name. + * Throws IllegalWriteException because POP3 messages are read-only. + * + * @param name header name + * @param value header value + * @see javax.mail.internet.MimeUtility + * @exception IllegalWriteException because the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + */ + @Override + public void addHeader(String name, String value) + throws MessagingException { + // XXX - should check for read-only folder? + throw new IllegalWriteException("POP3 messages are read-only"); + } + + /** + * Remove all headers with this name. + * Throws IllegalWriteException because POP3 messages are read-only. + * + * @exception IllegalWriteException because the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + */ + @Override + public void removeHeader(String name) + throws MessagingException { + // XXX - should check for read-only folder? + throw new IllegalWriteException("POP3 messages are read-only"); + } + + /** + * Return all the headers from this Message as an enumeration + * of Header objects.

    + * + * Note that certain headers may be encoded as per RFC 2047 + * if they contain non US-ASCII characters and these should + * be decoded.

    + * + * @return array of header objects + * @exception MessagingException for failures + * @see javax.mail.internet.MimeUtility + */ + @Override + public Enumeration

    getAllHeaders() throws MessagingException { + if (headers == null) + loadHeaders(); + return headers.getAllHeaders(); + } + + /** + * Return matching headers from this Message as an Enumeration of + * Header objects. + * + * @exception MessagingException for failures + */ + @Override + public Enumeration
    getMatchingHeaders(String[] names) + throws MessagingException { + if (headers == null) + loadHeaders(); + return headers.getMatchingHeaders(names); + } + + /** + * Return non-matching headers from this Message as an + * Enumeration of Header objects. + * + * @exception MessagingException for failures + */ + @Override + public Enumeration
    getNonMatchingHeaders(String[] names) + throws MessagingException { + if (headers == null) + loadHeaders(); + return headers.getNonMatchingHeaders(names); + } + + /** + * Add a raw RFC822 header-line. + * Throws IllegalWriteException because POP3 messages are read-only. + * + * @exception IllegalWriteException because the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + */ + @Override + public void addHeaderLine(String line) throws MessagingException { + // XXX - should check for read-only folder? + throw new IllegalWriteException("POP3 messages are read-only"); + } + + /** + * Get all header lines as an Enumeration of Strings. A Header + * line is a raw RFC822 header-line, containing both the "name" + * and "value" field. + * + * @exception MessagingException for failures + */ + @Override + public Enumeration getAllHeaderLines() throws MessagingException { + if (headers == null) + loadHeaders(); + return headers.getAllHeaderLines(); + } + + /** + * Get matching header lines as an Enumeration of Strings. + * A Header line is a raw RFC822 header-line, containing both + * the "name" and "value" field. + * + * @exception MessagingException for failures + */ + @Override + public Enumeration getMatchingHeaderLines(String[] names) + throws MessagingException { + if (headers == null) + loadHeaders(); + return headers.getMatchingHeaderLines(names); + } + + /** + * Get non-matching header lines as an Enumeration of Strings. + * A Header line is a raw RFC822 header-line, containing both + * the "name" and "value" field. + * + * @exception MessagingException for failures + */ + @Override + public Enumeration getNonMatchingHeaderLines(String[] names) + throws MessagingException { + if (headers == null) + loadHeaders(); + return headers.getNonMatchingHeaderLines(names); + } + + /** + * POP3 message can't be changed. This method throws + * IllegalWriteException. + * + * @exception IllegalWriteException because the underlying + * implementation does not support modification + */ + @Override + public void saveChanges() throws MessagingException { + // POP3 Messages are read-only + throw new IllegalWriteException("POP3 messages are read-only"); + } + + /** + * Output the message as an RFC 822 format stream, without + * specified headers. If the property "mail.pop3.cachewriteto" + * is set to "true", and ignoreList is null, and the message hasn't + * already been cached as a side effect of other operations, the message + * content is cached before being written. Otherwise, the message is + * streamed directly to the output stream without being cached. + * + * @exception IOException if an error occurs writing to the stream + * or if an error is generated by the + * javax.activation layer. + * @exception MessagingException for other failures + * @see javax.activation.DataHandler#writeTo + */ + @Override + public synchronized void writeTo(OutputStream os, String[] ignoreList) + throws IOException, MessagingException { + InputStream rawcontent = rawData.get(); + if (rawcontent == null && ignoreList == null && + !((POP3Store)(folder.getStore())).cacheWriteTo) { + if (folder.logger.isLoggable(Level.FINE)) + folder.logger.fine("streaming msg " + msgnum); + if (!folder.getProtocol().retr(msgnum, os)) { + expunged = true; + throw new MessageRemovedException("can't retrieve message #" + + msgnum + " in POP3Message.writeTo"); // XXX - what else? + } + } else if (rawcontent != null && ignoreList == null) { + // can just copy the cached data + InputStream in = ((SharedInputStream)rawcontent).newStream(0, -1); + try { + byte[] buf = new byte[16*1024]; + int len; + while ((len = in.read(buf)) > 0) + os.write(buf, 0, len); + } finally { + try { + if (in != null) + in.close(); + } catch (IOException ex) { } + } + } else + super.writeTo(os, ignoreList); + } + + /** + * Load the headers for this message into the InternetHeaders object. + * The headers are fetched using the POP3 TOP command. + */ + private void loadHeaders() throws MessagingException { + assert !Thread.holdsLock(this); + try { + boolean fetchContent = false; + synchronized (this) { + if (headers != null) // check again under lock + return; + InputStream hdrs = null; + if (((POP3Store)(folder.getStore())).disableTop || + (hdrs = folder.getProtocol().top(msgnum, 0)) == null) { + // possibly because the TOP command isn't supported, + // load headers as a side effect of loading the entire + // content. + fetchContent = true; + } else { + try { + hdrSize = hdrs.available(); + headers = new InternetHeaders(hdrs); + } finally { + hdrs.close(); + } + } + } + + /* + * Outside the synchronization block... + * + * Do we need to fetch the entire mesage content in order to + * load the headers as a side effect? Yes, there's a race + * condition here - multiple threads could decide that the + * content needs to be fetched. Fortunately, they'll all + * synchronize in the getContentStream method and the content + * will only be loaded once. + */ + if (fetchContent) { + InputStream cs = null; + try { + cs = getContentStream(); + } finally { + if (cs != null) + cs.close(); + } + } + } catch (EOFException eex) { + folder.close(false); + throw new FolderClosedException(folder, eex.toString()); + } catch (IOException ex) { + throw new MessagingException("error loading POP3 headers", ex); + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Provider.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Provider.java new file mode 100644 index 000000000..9020ca5d3 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Provider.java @@ -0,0 +1,54 @@ + +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import javax.mail.Provider; + +/** + * The POP3 protocol provider. + */ +public class POP3Provider extends Provider { + public POP3Provider() { + super(Provider.Type.STORE, "pop3", POP3Store.class.getName(), + "Oracle", null); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3SSLProvider.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3SSLProvider.java new file mode 100644 index 000000000..d09be357d --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3SSLProvider.java @@ -0,0 +1,54 @@ + +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import javax.mail.Provider; + +/** + * The POP3 SSL protocol provider. + */ +public class POP3SSLProvider extends Provider { + public POP3SSLProvider() { + super(Provider.Type.STORE, "pop3s", POP3SSLStore.class.getName(), + "Oracle", null); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3SSLStore.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3SSLStore.java new file mode 100644 index 000000000..299329b9a --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3SSLStore.java @@ -0,0 +1,55 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import javax.mail.*; + +/** + * A POP3 Message Store using SSL. Contains only one folder, "INBOX". + * + * @author Bill Shannon + */ +public class POP3SSLStore extends POP3Store { + + public POP3SSLStore(Session session, URLName url) { + super(session, url, "pop3s", true); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Store.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Store.java new file mode 100644 index 000000000..08ef703be --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/POP3Store.java @@ -0,0 +1,453 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import java.util.Properties; +import java.util.logging.Level; +import java.lang.reflect.*; + +import javax.mail.*; +import javax.mail.internet.*; +import java.io.File; +import java.io.PrintStream; +import java.io.IOException; +import java.io.EOFException; +import java.util.Collections; +import java.util.Map; + +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.MailLogger; +import com.sun.mail.util.SocketConnectException; +import com.sun.mail.util.MailConnectException; + +/** + * A POP3 Message Store. Contains only one folder, "INBOX". + * + * See the com.sun.mail.pop3 package + * documentation for further information on the POP3 protocol provider.

    + * + * @author Bill Shannon + * @author John Mani + */ +public class POP3Store extends Store { + + private String name = "pop3"; // my protocol name + private int defaultPort = 110; // default POP3 port + private boolean isSSL = false; // use SSL? + + private Protocol port = null; // POP3 port for self + private POP3Folder portOwner = null; // folder owning port + private String host = null; // host + private int portNum = -1; + private String user = null; + private String passwd = null; + private boolean useStartTLS = false; + private boolean requireStartTLS = false; + private boolean usingSSL = false; + private Map capabilities; + private MailLogger logger; + + // following set here and accessed by other classes in this package + volatile Constructor messageConstructor = null; + volatile boolean rsetBeforeQuit = false; + volatile boolean disableTop = false; + volatile boolean forgetTopHeaders = false; + volatile boolean supportsUidl = true; + volatile boolean cacheWriteTo = false; + volatile boolean useFileCache = false; + volatile File fileCacheDir = null; + volatile boolean keepMessageContent = false; + volatile boolean finalizeCleanClose = false; + + public POP3Store(Session session, URLName url) { + this(session, url, "pop3", false); + } + + public POP3Store(Session session, URLName url, + String name, boolean isSSL) { + super(session, url); + if (url != null) + name = url.getProtocol(); + this.name = name; + logger = new MailLogger(this.getClass(), "DEBUG POP3", + session.getDebug(), session.getDebugOut()); + + if (!isSSL) + isSSL = PropUtil.getBooleanProperty(session.getProperties(), + "mail." + name + ".ssl.enable", false); + if (isSSL) + this.defaultPort = 995; + else + this.defaultPort = 110; + this.isSSL = isSSL; + + rsetBeforeQuit = getBoolProp("rsetbeforequit"); + disableTop = getBoolProp("disabletop"); + forgetTopHeaders = getBoolProp("forgettopheaders"); + cacheWriteTo = getBoolProp("cachewriteto"); + useFileCache = getBoolProp("filecache.enable"); + String dir = session.getProperty("mail." + name + ".filecache.dir"); + if (dir != null && logger.isLoggable(Level.CONFIG)) + logger.config("mail." + name + ".filecache.dir: " + dir); + if (dir != null) + fileCacheDir = new File(dir); + keepMessageContent = getBoolProp("keepmessagecontent"); + + // mail.pop3.starttls.enable enables use of STLS command + useStartTLS = getBoolProp("starttls.enable"); + + // mail.pop3.starttls.required requires use of STLS command + requireStartTLS = getBoolProp("starttls.required"); + + // mail.pop3.finalizecleanclose requires clean close when finalizing + finalizeCleanClose = getBoolProp("finalizecleanclose"); + + String s = session.getProperty("mail." + name + ".message.class"); + if (s != null) { + logger.log(Level.CONFIG, "message class: {0}", s); + try { + ClassLoader cl = this.getClass().getClassLoader(); + + // now load the class + Class messageClass = null; + try { + // First try the "application's" class loader. + // This should eventually be replaced by + // Thread.currentThread().getContextClassLoader(). + messageClass = Class.forName(s, false, cl); + } catch (ClassNotFoundException ex1) { + // That didn't work, now try the "system" class loader. + // (Need both of these because JDK 1.1 class loaders + // may not delegate to their parent class loader.) + messageClass = Class.forName(s); + } + + Class[] c = {javax.mail.Folder.class, int.class}; + messageConstructor = messageClass.getConstructor(c); + } catch (Exception ex) { + logger.log(Level.CONFIG, "failed to load message class", ex); + } + } + } + + /** + * Get the value of a boolean property. + * Print out the value if logging is enabled. + */ + private final synchronized boolean getBoolProp(String prop) { + prop = "mail." + name + "." + prop; + boolean val = PropUtil.getBooleanProperty(session.getProperties(), + prop, false); + if (logger.isLoggable(Level.CONFIG)) + logger.config(prop + ": " + val); + return val; + } + + /** + * Get a reference to the session. + */ + synchronized Session getSession() { + return session; + } + + @Override + protected synchronized boolean protocolConnect(String host, int portNum, + String user, String passwd) throws MessagingException { + + // check for non-null values of host, password, user + if (host == null || passwd == null || user == null) + return false; + + // if port is not specified, set it to value of mail.pop3.port + // property if it exists, otherwise default to 110 + if (portNum == -1) + portNum = PropUtil.getIntProperty(session.getProperties(), + "mail." + name + ".port", -1); + + if (portNum == -1) + portNum = defaultPort; + + this.host = host; + this.portNum = portNum; + this.user = user; + this.passwd = passwd; + try { + port = getPort(null); + } catch (EOFException eex) { + throw new AuthenticationFailedException(eex.getMessage()); + } catch (SocketConnectException scex) { + throw new MailConnectException(scex); + } catch (IOException ioex) { + throw new MessagingException("Connect failed", ioex); + } + + return true; + } + + /** + * Check whether this store is connected. Override superclass + * method, to actually ping our server connection. + */ + /* + * Note that we maintain somewhat of an illusion of being connected + * even if we're not really connected. This is because a Folder + * can use the connection and close it when it's done. If we then + * ask whether the Store's connected we want the answer to be true, + * as long as we can reconnect at that point. This means that we + * need to be able to reconnect the Store on demand. + */ + @Override + public synchronized boolean isConnected() { + if (!super.isConnected()) + // if we haven't been connected at all, don't bother with + // the NOOP. + return false; + try { + if (port == null) + port = getPort(null); + else if (!port.noop()) + throw new IOException("NOOP failed"); + return true; + } catch (IOException ioex) { + // no longer connected, close it down + try { + super.close(); // notifies listeners + } catch (MessagingException mex) { + // ignore it + } + return false; + } + } + + synchronized Protocol getPort(POP3Folder owner) throws IOException { + Protocol p; + + // if we already have a port, remember who's using it + if (port != null && portOwner == null) { + portOwner = owner; + return port; + } + + // need a new port, create it and try to login + p = new Protocol(host, portNum, logger, + session.getProperties(), "mail." + name, isSSL); + + if (useStartTLS || requireStartTLS) { + if (p.hasCapability("STLS")) { + if (p.stls()) { + // success, refresh capabilities + p.setCapabilities(p.capa()); + } else if (requireStartTLS) { + logger.fine("STLS required but failed"); + throw cleanupAndThrow(p, + new EOFException("STLS required but failed")); + } + } else if (requireStartTLS) { + logger.fine("STLS required but not supported"); + throw cleanupAndThrow(p, + new EOFException("STLS required but not supported")); + } + } + + capabilities = p.getCapabilities(); // save for later, may be null + usingSSL = p.isSSL(); // in case anyone asks + + /* + * If we haven't explicitly disabled use of the TOP command, + * and the server has provided its capabilities, + * and the server doesn't support the TOP command, + * disable the TOP command. + */ + if (!disableTop && + capabilities != null && !capabilities.containsKey("TOP")) { + disableTop = true; + logger.fine("server doesn't support TOP, disabling it"); + } + + supportsUidl = capabilities == null || capabilities.containsKey("UIDL"); + + String msg = null; + if ((msg = p.login(user, passwd)) != null) { + throw cleanupAndThrow(p, new EOFException(msg)); + } + + /* + * If a Folder closes the port, and then a Folder + * is opened, the Store won't have a port. In that + * case, the getPort call will come from Folder.open, + * but we need to keep track of the port in the Store + * so that a later call to Folder.isOpen, which calls + * Store.isConnected, will use the same port. + */ + if (port == null && owner != null) { + port = p; + portOwner = owner; + } + if (portOwner == null) + portOwner = owner; + return p; + } + + private static IOException cleanupAndThrow(Protocol p, IOException ife) { + try { + p.quit(); + } catch (Throwable thr) { + if (isRecoverable(thr)) { + ife.addSuppressed(thr); + } else { + thr.addSuppressed(ife); + if (thr instanceof Error) { + throw (Error) thr; + } + if (thr instanceof RuntimeException) { + throw (RuntimeException) thr; + } + throw new RuntimeException("unexpected exception", thr); + } + } + return ife; + } + + private static boolean isRecoverable(Throwable t) { + return (t instanceof Exception) || (t instanceof LinkageError); + } + + synchronized void closePort(POP3Folder owner) { + if (portOwner == owner) { + port = null; + portOwner = null; + } + } + + @Override + public synchronized void close() throws MessagingException { + close(false); + } + + synchronized void close(boolean force) throws MessagingException { + try { + if (port != null) { + if (force) + port.close(); + else + port.quit(); + } + } catch (IOException ioex) { + } finally { + port = null; + + // to set the state and send the closed connection event + super.close(); + } + } + + @Override + public Folder getDefaultFolder() throws MessagingException { + checkConnected(); + return new DefaultFolder(this); + } + + /** + * Only the name "INBOX" is supported. + */ + @Override + public Folder getFolder(String name) throws MessagingException { + checkConnected(); + return new POP3Folder(this, name); + } + + @Override + public Folder getFolder(URLName url) throws MessagingException { + checkConnected(); + return new POP3Folder(this, url.getFile()); + } + + /** + * Return a Map of the capabilities the server provided, + * as per RFC 2449. If the server doesn't support RFC 2449, + * an emtpy Map is returned. The returned Map can not be modified. + * The key to the Map is the upper case capability name as + * a String. The value of the entry is the entire String + * capability line returned by the server.

    + * + * For example, to check if the server supports the STLS capability, use: + * if (store.capabilities().containsKey("STLS")) ... + * + * @return Map of capabilities + * @exception MessagingException for failures + * @since JavaMail 1.4.3 + */ + public Map capabilities() throws MessagingException { + Map c; + synchronized (this) { + c = capabilities; + } + if (c != null) + return Collections.unmodifiableMap(c); + else + return Collections.emptyMap(); + } + + /** + * Is this POP3Store using SSL to connect to the server? + * + * @return true if using SSL + * @since JavaMail 1.4.6 + */ + public synchronized boolean isSSL() { + return usingSSL; + } + + @Override + protected void finalize() throws Throwable { + try { + if (port != null) // don't force a connection attempt + close(!finalizeCleanClose); + } finally { + super.finalize(); + } + } + + private void checkConnected() throws MessagingException { + if (!super.isConnected()) + throw new MessagingException("Not connected"); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/Protocol.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/Protocol.java new file mode 100644 index 000000000..e53a4c400 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/Protocol.java @@ -0,0 +1,885 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import java.util.*; +import java.net.*; +import java.io.*; +import java.security.*; +import java.util.logging.Level; +import javax.net.ssl.SSLSocket; + +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.MailLogger; +import com.sun.mail.util.SocketFetcher; +import com.sun.mail.util.LineInputStream; +import com.sun.mail.util.TraceInputStream; +import com.sun.mail.util.TraceOutputStream; +import com.sun.mail.util.SharedByteArrayOutputStream; + +class Response { + boolean ok = false; // true if "+OK" + String data = null; // rest of line after "+OK" or "-ERR" + InputStream bytes = null; // all the bytes from a multi-line response +} + +/** + * This class provides a POP3 connection and implements + * the POP3 protocol requests. + * + * APOP support courtesy of "chamness". + * + * @author Bill Shannon + */ +class Protocol { + private Socket socket; // POP3 socket + private String host; // host we're connected to + private Properties props; // session properties + private String prefix; // protocol name prefix, for props + private BufferedReader input; // input buf + private PrintWriter output; // output buf + private TraceInputStream traceInput; + private TraceOutputStream traceOutput; + private MailLogger logger; + private MailLogger traceLogger; + private String apopChallenge = null; + private Map capabilities = null; + private boolean pipelining; + private boolean noauthdebug = true; // hide auth info in debug output + private boolean traceSuspended; // temporarily suspend tracing + + private static final int POP3_PORT = 110; // standard POP3 port + private static final String CRLF = "\r\n"; + // sometimes the returned size isn't quite big enough + private static final int SLOP = 128; + + /** + * Open a connection to the POP3 server. + */ + Protocol(String host, int port, MailLogger logger, + Properties props, String prefix, boolean isSSL) + throws IOException { + this.host = host; + this.props = props; + this.prefix = prefix; + this.logger = logger; + traceLogger = logger.getSubLogger("protocol", null); + noauthdebug = !PropUtil.getBooleanProperty(props, + "mail.debug.auth", false); + + Response r; + boolean enableAPOP = getBoolProp(props, prefix + ".apop.enable"); + boolean disableCapa = getBoolProp(props, prefix + ".disablecapa"); + try { + if (port == -1) + port = POP3_PORT; + if (logger.isLoggable(Level.FINE)) + logger.fine("connecting to host \"" + host + + "\", port " + port + ", isSSL " + isSSL); + + socket = SocketFetcher.getSocket(host, port, props, prefix, isSSL); + initStreams(); + r = simpleCommand(null); + } catch (IOException ioe) { + throw cleanupAndThrow(socket, ioe); + } + + if (!r.ok) { + throw cleanupAndThrow(socket, new IOException("Connect failed")); + } + if (enableAPOP && r.data != null) { + int challStart = r.data.indexOf('<'); // start of challenge + int challEnd = r.data.indexOf('>', challStart); // end of challenge + if (challStart != -1 && challEnd != -1) + apopChallenge = r.data.substring(challStart, challEnd + 1); + logger.log(Level.FINE, "APOP challenge: {0}", apopChallenge); + } + + // if server supports RFC 2449, set capabilities + if (!disableCapa) + setCapabilities(capa()); + + pipelining = hasCapability("PIPELINING") || + PropUtil.getBooleanProperty(props, prefix + ".pipelining", false); + if (pipelining) + logger.config("PIPELINING enabled"); + } + + private static IOException cleanupAndThrow(Socket socket, IOException ife) { + try { + socket.close(); + } catch (Throwable thr) { + if (isRecoverable(thr)) { + ife.addSuppressed(thr); + } else { + thr.addSuppressed(ife); + if (thr instanceof Error) { + throw (Error) thr; + } + if (thr instanceof RuntimeException) { + throw (RuntimeException) thr; + } + throw new RuntimeException("unexpected exception", thr); + } + } + return ife; + } + + private static boolean isRecoverable(Throwable t) { + return (t instanceof Exception) || (t instanceof LinkageError); + } + + /** + * Get the value of a boolean property. + * Print out the value if logging is enabled. + */ + private final synchronized boolean getBoolProp(Properties props, + String prop) { + boolean val = PropUtil.getBooleanProperty(props, prop, false); + if (logger.isLoggable(Level.CONFIG)) + logger.config(prop + ": " + val); + return val; + } + + private void initStreams() throws IOException { + boolean quote = PropUtil.getBooleanProperty(props, + "mail.debug.quote", false); + traceInput = + new TraceInputStream(socket.getInputStream(), traceLogger); + traceInput.setQuote(quote); + + traceOutput = + new TraceOutputStream(socket.getOutputStream(), traceLogger); + traceOutput.setQuote(quote); + + // should be US-ASCII, but not all JDK's support it so use iso-8859-1 + input = new BufferedReader(new InputStreamReader(traceInput, + "iso-8859-1")); + output = new PrintWriter( + new BufferedWriter( + new OutputStreamWriter(traceOutput, "iso-8859-1"))); + } + + @Override + protected void finalize() throws Throwable { + try { + if (socket != null) // Forgot to logout ?! + quit(); + } finally { + super.finalize(); + } + } + + /** + * Parse the capabilities from a CAPA response. + */ + synchronized void setCapabilities(InputStream in) { + if (in == null) { + capabilities = null; + return; + } + + capabilities = new HashMap<>(10); + BufferedReader r = null; + try { + r = new BufferedReader(new InputStreamReader(in, "us-ascii")); + } catch (UnsupportedEncodingException ex) { + // should never happen + assert false; + } + String s; + try { + while ((s = r.readLine()) != null) { + String cap = s; + int i = cap.indexOf(' '); + if (i > 0) + cap = cap.substring(0, i); + capabilities.put(cap.toUpperCase(Locale.ENGLISH), s); + } + } catch (IOException ex) { + // should never happen + } finally { + try { + in.close(); + } catch (IOException ex) { } + } + } + + /** + * Check whether the given capability is supported by + * this server. Returns true if so, otherwise + * returns false. + */ + synchronized boolean hasCapability(String c) { + return capabilities != null && + capabilities.containsKey(c.toUpperCase(Locale.ENGLISH)); + } + + /** + * Return the map of capabilities returned by the server. + */ + synchronized Map getCapabilities() { + return capabilities; + } + + /** + * Login to the server, using the USER and PASS commands. + */ + synchronized String login(String user, String password) + throws IOException { + Response r; + // only pipeline password if connection is secure + boolean batch = pipelining && socket instanceof SSLSocket; + + try { + + if (noauthdebug && isTracing()) { + logger.fine("authentication command trace suppressed"); + suspendTracing(); + } + String dpw = null; + if (apopChallenge != null) + dpw = getDigest(password); + if (apopChallenge != null && dpw != null) { + r = simpleCommand("APOP " + user + " " + dpw); + } else if (batch) { + String cmd = "USER " + user; + batchCommandStart(cmd); + issueCommand(cmd); + cmd = "PASS " + password; + batchCommandContinue(cmd); + issueCommand(cmd); + r = readResponse(); + if (!r.ok) { + String err = r.data != null ? r.data : "USER command failed"; + readResponse(); // read and ignore PASS response + batchCommandEnd(); + return err; + } + r = readResponse(); + batchCommandEnd(); + } else { + r = simpleCommand("USER " + user); + if (!r.ok) + return r.data != null ? r.data : "USER command failed"; + r = simpleCommand("PASS " + password); + } + if (noauthdebug && isTracing()) + logger.log(Level.FINE, "authentication command {0}", + (r.ok ? "succeeded" : "failed")); + if (!r.ok) + return r.data != null ? r.data : "login failed"; + return null; + + } finally { + resumeTracing(); + } + } + + /** + * Gets the APOP message digest. + * From RFC 1939: + * + * The 'digest' parameter is calculated by applying the MD5 + * algorithm [RFC1321] to a string consisting of the timestamp + * (including angle-brackets) followed by a shared secret. + * The 'digest' parameter itself is a 16-octet value which is + * sent in hexadecimal format, using lower-case ASCII characters. + * + * @param password The APOP password + * @return The APOP digest or an empty string if an error occurs. + */ + private String getDigest(String password) { + String key = apopChallenge + password; + byte[] digest; + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + digest = md.digest(key.getBytes("iso-8859-1")); // XXX + } catch (NoSuchAlgorithmException nsae) { + return null; + } catch (UnsupportedEncodingException uee) { + return null; + } + return toHex(digest); + } + + private static char[] digits = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + /** + * Convert a byte array to a string of hex digits representing the bytes. + */ + private static String toHex(byte[] bytes) { + char[] result = new char[bytes.length * 2]; + + for (int index = 0, i = 0; index < bytes.length; index++) { + int temp = bytes[index] & 0xFF; + result[i++] = digits[temp >> 4]; + result[i++] = digits[temp & 0xF]; + } + return new String(result); + } + + /** + * Close down the connection, sending the QUIT command. + */ + synchronized boolean quit() throws IOException { + boolean ok = false; + try { + Response r = simpleCommand("QUIT"); + ok = r.ok; + } finally { + close(); + } + return ok; + } + + /** + * Close the connection without sending any commands. + */ + void close() { + try { + socket.close(); + } catch (IOException ex) { + // ignore it + } finally { + socket = null; + input = null; + output = null; + } + } + + /** + * Return the total number of messages and mailbox size, + * using the STAT command. + */ + synchronized Status stat() throws IOException { + Response r = simpleCommand("STAT"); + Status s = new Status(); + + /* + * Normally the STAT command shouldn't fail but apparently it + * does when accessing Hotmail too often, returning: + * -ERR login allowed only every 15 minutes + * (Why it doesn't just fail the login, I don't know.) + * This is a serious failure that we don't want to hide + * from the user. + */ + if (!r.ok) + throw new IOException("STAT command failed: " + r.data); + + if (r.data != null) { + try { + StringTokenizer st = new StringTokenizer(r.data); + s.total = Integer.parseInt(st.nextToken()); + s.size = Integer.parseInt(st.nextToken()); + } catch (RuntimeException e) { + } + } + return s; + } + + /** + * Return the size of the message using the LIST command. + */ + synchronized int list(int msg) throws IOException { + Response r = simpleCommand("LIST " + msg); + int size = -1; + if (r.ok && r.data != null) { + try { + StringTokenizer st = new StringTokenizer(r.data); + st.nextToken(); // skip message number + size = Integer.parseInt(st.nextToken()); + } catch (RuntimeException e) { + // ignore it + } + } + return size; + } + + /** + * Return the size of all messages using the LIST command. + */ + synchronized InputStream list() throws IOException { + Response r = multilineCommand("LIST", 128); // 128 == output size est + return r.bytes; + } + + /** + * Retrieve the specified message. + * Given an estimate of the message's size we can be more efficient, + * preallocating the array and returning a SharedInputStream to allow + * us to share the array. + */ + synchronized InputStream retr(int msg, int size) throws IOException { + Response r; + String cmd; + boolean batch = size == 0 && pipelining; + if (batch) { + cmd = "LIST " + msg; + batchCommandStart(cmd); + issueCommand(cmd); + cmd = "RETR " + msg; + batchCommandContinue(cmd); + issueCommand(cmd); + r = readResponse(); + if (r.ok && r.data != null) { + // parse the LIST response to get the message size + try { + StringTokenizer st = new StringTokenizer(r.data); + st.nextToken(); // skip message number + size = Integer.parseInt(st.nextToken()); + // don't allow ridiculous sizes + if (size > 1024*1024*1024 || size < 0) + size = 0; + else { + if (logger.isLoggable(Level.FINE)) + logger.fine("pipeline message size " + size); + size += SLOP; + } + } catch (RuntimeException e) { + } + } + r = readResponse(); + if (r.ok) + r.bytes = readMultilineResponse(size + SLOP); + batchCommandEnd(); + } else { + cmd = "RETR " + msg; + multilineCommandStart(cmd); + issueCommand(cmd); + r = readResponse(); + if (!r.ok) { + multilineCommandEnd(); + return null; + } + + /* + * Many servers return a response to the RETR command of the form: + * +OK 832 octets + * If we don't have a size guess already, try to parse the response + * for data in that format and use it if found. It's only a guess, + * but it might be a good guess. + */ + if (size <= 0 && r.data != null) { + try { + StringTokenizer st = new StringTokenizer(r.data); + String s = st.nextToken(); + String octets = st.nextToken(); + if (octets.equals("octets")) { + size = Integer.parseInt(s); + // don't allow ridiculous sizes + if (size > 1024*1024*1024 || size < 0) + size = 0; + else { + if (logger.isLoggable(Level.FINE)) + logger.fine("guessing message size: " + size); + size += SLOP; + } + } + } catch (RuntimeException e) { + } + } + r.bytes = readMultilineResponse(size); + multilineCommandEnd(); + } + if (r.ok) { + if (size > 0 && logger.isLoggable(Level.FINE)) + logger.fine("got message size " + r.bytes.available()); + } + return r.bytes; + } + + /** + * Retrieve the specified message and stream the content to the + * specified OutputStream. Return true on success. + */ + synchronized boolean retr(int msg, OutputStream os) throws IOException { + String cmd = "RETR " + msg; + multilineCommandStart(cmd); + issueCommand(cmd); + Response r = readResponse(); + if (!r.ok) { + multilineCommandEnd(); + return false; + } + + Throwable terr = null; + int b, lastb = '\n'; + try { + while ((b = input.read()) >= 0) { + if (lastb == '\n' && b == '.') { + b = input.read(); + if (b == '\r') { + // end of response, consume LF as well + b = input.read(); + break; + } + } + + /* + * Keep writing unless we get an error while writing, + * which we defer until all of the data has been read. + */ + if (terr == null) { + try { + os.write(b); + } catch (IOException ex) { + logger.log(Level.FINE, "exception while streaming", ex); + terr = ex; + } catch (RuntimeException ex) { + logger.log(Level.FINE, "exception while streaming", ex); + terr = ex; + } + } + lastb = b; + } + } catch (InterruptedIOException iioex) { + /* + * As above in simpleCommand, close the socket to recover. + */ + try { + socket.close(); + } catch (IOException cex) { } + throw iioex; + } + if (b < 0) + throw new EOFException("EOF on socket"); + + // was there a deferred error? + if (terr != null) { + if (terr instanceof IOException) + throw (IOException)terr; + if (terr instanceof RuntimeException) + throw (RuntimeException)terr; + assert false; // can't get here + } + multilineCommandEnd(); + return true; + } + + /** + * Return the message header and the first n lines of the message. + */ + synchronized InputStream top(int msg, int n) throws IOException { + Response r = multilineCommand("TOP " + msg + " " + n, 0); + return r.bytes; + } + + /** + * Delete (permanently) the specified message. + */ + synchronized boolean dele(int msg) throws IOException { + Response r = simpleCommand("DELE " + msg); + return r.ok; + } + + /** + * Return the UIDL string for the message. + */ + synchronized String uidl(int msg) throws IOException { + Response r = simpleCommand("UIDL " + msg); + if (!r.ok) + return null; + int i = r.data.indexOf(' '); + if (i > 0) + return r.data.substring(i + 1); + else + return null; + } + + /** + * Return the UIDL strings for all messages. + * The UID for msg #N is returned in uids[N-1]. + */ + synchronized boolean uidl(String[] uids) throws IOException { + Response r = multilineCommand("UIDL", 15 * uids.length); + if (!r.ok) + return false; + LineInputStream lis = new LineInputStream(r.bytes); + String line = null; + while ((line = lis.readLine()) != null) { + int i = line.indexOf(' '); + if (i < 1 || i >= line.length()) + continue; + int n = Integer.parseInt(line.substring(0, i)); + if (n > 0 && n <= uids.length) + uids[n - 1] = line.substring(i + 1); + } + try { + r.bytes.close(); + } catch (IOException ex) { + // ignore it + } + return true; + } + + /** + * Do a NOOP. + */ + synchronized boolean noop() throws IOException { + Response r = simpleCommand("NOOP"); + return r.ok; + } + + /** + * Do an RSET. + */ + synchronized boolean rset() throws IOException { + Response r = simpleCommand("RSET"); + return r.ok; + } + + /** + * Start TLS using STLS command specified by RFC 2595. + * If already using SSL, this is a nop and the STLS command is not issued. + */ + synchronized boolean stls() throws IOException { + if (socket instanceof SSLSocket) + return true; // nothing to do + Response r = simpleCommand("STLS"); + if (r.ok) { + // it worked, now switch the socket into TLS mode + try { + socket = SocketFetcher.startTLS(socket, host, props, prefix); + initStreams(); + } catch (IOException ioex) { + try { + socket.close(); + } finally { + socket = null; + input = null; + output = null; + } + IOException sioex = + new IOException("Could not convert socket to TLS"); + sioex.initCause(ioex); + throw sioex; + } + } + return r.ok; + } + + /** + * Is this connection using SSL? + */ + synchronized boolean isSSL() { + return socket instanceof SSLSocket; + } + + /** + * Get server capabilities using CAPA command specified by RFC 2449. + * Returns null if not supported. + */ + synchronized InputStream capa() throws IOException { + Response r = multilineCommand("CAPA", 128); // 128 == output size est + if (!r.ok) + return null; + return r.bytes; + } + + /** + * Issue a simple POP3 command and return the response. + */ + private Response simpleCommand(String cmd) throws IOException { + simpleCommandStart(cmd); + issueCommand(cmd); + Response r = readResponse(); + simpleCommandEnd(); + return r; + } + + /** + * Send the specified command. + */ + private void issueCommand(String cmd) throws IOException { + if (socket == null) + throw new IOException("Folder is closed"); // XXX + + if (cmd != null) { + cmd += CRLF; + output.print(cmd); // do it in one write + output.flush(); + } + } + + /** + * Read the response to a command. + */ + private Response readResponse() throws IOException { + String line = null; + try { + line = input.readLine(); + } catch (InterruptedIOException iioex) { + /* + * If we get a timeout while using the socket, we have no idea + * what state the connection is in. The server could still be + * alive, but slow, and could still be sending data. The only + * safe way to recover is to drop the connection. + */ + try { + socket.close(); + } catch (IOException cex) { } + throw new EOFException(iioex.getMessage()); + } catch (SocketException ex) { + /* + * If we get an error while using the socket, we have no idea + * what state the connection is in. The server could still be + * alive, but slow, and could still be sending data. The only + * safe way to recover is to drop the connection. + */ + try { + socket.close(); + } catch (IOException cex) { } + throw new EOFException(ex.getMessage()); + } + + if (line == null) { + traceLogger.finest(""); + throw new EOFException("EOF on socket"); + } + Response r = new Response(); + if (line.startsWith("+OK")) + r.ok = true; + else if (line.startsWith("-ERR")) + r.ok = false; + else + throw new IOException("Unexpected response: " + line); + int i; + if ((i = line.indexOf(' ')) >= 0) + r.data = line.substring(i + 1); + return r; + } + + /** + * Issue a POP3 command that expects a multi-line response. + * size is an estimate of the response size. + */ + private Response multilineCommand(String cmd, int size) throws IOException { + multilineCommandStart(cmd); + issueCommand(cmd); + Response r = readResponse(); + if (!r.ok) { + multilineCommandEnd(); + return r; + } + r.bytes = readMultilineResponse(size); + multilineCommandEnd(); + return r; + } + + /** + * Read the response to a multiline command after the command response. + * The size parameter indicates the expected size of the response; + * the actual size can be different. Returns an InputStream to the + * response bytes. + */ + private InputStream readMultilineResponse(int size) throws IOException { + SharedByteArrayOutputStream buf = new SharedByteArrayOutputStream(size); + int b, lastb = '\n'; + try { + while ((b = input.read()) >= 0) { + if (lastb == '\n' && b == '.') { + b = input.read(); + if (b == '\r') { + // end of response, consume LF as well + b = input.read(); + break; + } + } + buf.write(b); + lastb = b; + } + } catch (InterruptedIOException iioex) { + /* + * As above in readResponse, close the socket to recover. + */ + try { + socket.close(); + } catch (IOException cex) { } + throw iioex; + } + if (b < 0) + throw new EOFException("EOF on socket"); + return buf.toStream(); + } + + /** + * Is protocol tracing enabled? + */ + protected boolean isTracing() { + return traceLogger.isLoggable(Level.FINEST); + } + + /** + * Temporarily turn off protocol tracing, e.g., to prevent + * tracing the authentication sequence, including the password. + */ + private void suspendTracing() { + if (traceLogger.isLoggable(Level.FINEST)) { + traceInput.setTrace(false); + traceOutput.setTrace(false); + } + } + + /** + * Resume protocol tracing, if it was enabled to begin with. + */ + private void resumeTracing() { + if (traceLogger.isLoggable(Level.FINEST)) { + traceInput.setTrace(true); + traceOutput.setTrace(true); + } + } + + /* + * Probe points for GlassFish monitoring. + */ + private void simpleCommandStart(String command) { } + private void simpleCommandEnd() { } + private void multilineCommandStart(String command) { } + private void multilineCommandEnd() { } + private void batchCommandStart(String command) { } + private void batchCommandContinue(String command) { } + private void batchCommandEnd() { } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/Status.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/Status.java new file mode 100644 index 000000000..2a65ccb77 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/Status.java @@ -0,0 +1,49 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +/** + * Result of POP3 STAT command. + */ +class Status { + int total = 0; // number of messages in the mailbox + int size = 0; // size of the mailbox +}; diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/TempFile.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/TempFile.java new file mode 100644 index 000000000..4748be11c --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/TempFile.java @@ -0,0 +1,91 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import java.io.*; + +/** + * A temporary file used to cache POP3 messages. + */ +class TempFile { + + private File file; // the temp file name + private WritableSharedFile sf; + + /** + * Create a temp file in the specified directory (if not null). + * The file will be deleted when the JVM exits. + */ + public TempFile(File dir) throws IOException { + file = File.createTempFile("pop3.", ".mbox", dir); + // XXX - need JDK 6 to set permissions on the file to owner-only + file.deleteOnExit(); + sf = new WritableSharedFile(file); + } + + /** + * Return a stream for appending to the temp file. + */ + public AppendStream getAppendStream() throws IOException { + return sf.getAppendStream(); + } + + /** + * Close and remove this temp file. + */ + public void close() { + try { + sf.close(); + } catch (IOException ex) { + // ignore it + } + file.delete(); + } + + @Override + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/WritableSharedFile.java b/fine-third-default/fine-mail/src/com/sun/mail/pop3/WritableSharedFile.java new file mode 100644 index 000000000..7a890c609 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/WritableSharedFile.java @@ -0,0 +1,107 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.pop3; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import javax.mail.util.SharedFileInputStream; + +/** + * A subclass of SharedFileInputStream that also allows writing. + */ +class WritableSharedFile extends SharedFileInputStream { + + private RandomAccessFile raf; + private AppendStream af; + + public WritableSharedFile(File file) throws IOException { + super(file); + try { + raf = new RandomAccessFile(file, "rw"); + } catch (IOException ex) { + // if anything goes wrong opening the writable file, + // close the readable file too + super.close(); + } + } + + /** + * Return the writable version of this file. + */ + public RandomAccessFile getWritableFile() { + return raf; + } + + /** + * Close the readable and writable files. + */ + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + raf.close(); + } + } + + /** + * Update the size of the readable file after writing to the file. Updates + * the length to be the current size of the file. + */ + synchronized long updateLength() throws IOException { + datalen = in.length(); + af = null; + return datalen; + } + + /** + * Return a new AppendStream, but only if one isn't in active use. + */ + public synchronized AppendStream getAppendStream() throws IOException { + if (af != null) { + throw new IOException( + "POP3 file cache only supports single threaded access"); + } + af = new AppendStream(this); + return af; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/pop3/package.html b/fine-third-default/fine-mail/src/com/sun/mail/pop3/package.html new file mode 100644 index 000000000..a669fd0fc --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/pop3/package.html @@ -0,0 +1,696 @@ + + + + + + +com.sun.mail.pop3 package + + + +A POP3 protocol provider for the JavaMail API +that provides access to a POP3 message store. +Refer to +RFC 1939 +for more information. +

    +The POP3 provider provides a Store object that contains a single Folder +named "INBOX". Due to the limitations of the POP3 protocol, many of +the JavaMail API capabilities like event notification, folder management, +flag management, etc. are not allowed. The corresponding methods throw +the MethodNotSupportedException exception; see below for details. +

    +

    +Note that JavaMail does not include a local store into +which messages can be downloaded and stored. See our + +Third Party Products +web page for availability of "mbox" and "MH" local store providers. +

    +

    +The POP3 provider is accessed through the JavaMail APIs by using the protocol +name "pop3" or a URL of the form "pop3://user:password@host:port/INBOX". +

    +

    +POP3 supports only a single folder named "INBOX". +

    +

    +POP3 supports no permanent flags (see +{@link javax.mail.Folder#getPermanentFlags Folder.getPermanentFlags()}). +In particular, the Flags.Flag.RECENT flag will never be set +for POP3 +messages. It's up to the application to determine which messages in a +POP3 mailbox are "new". There are several strategies to accomplish +this, depending on the needs of the application and the environment: +

    +
      +
    • +A simple approach would be to keep track of the newest +message seen by the application. +
    • +
    • +An alternative would be to keep track of the UIDs (see below) +of all messages that have been seen. +
    • +
    • +Another approach is to download all messages into a local +mailbox, so that all messages in the POP3 mailbox are, by +definition, new. +
    • +
    +

    +All approaches will require some permanent storage associated with the client. +

    +

    +POP3 does not support the Folder.expunge() method. To delete and +expunge messages, set the Flags.Flag.DELETED flag on the messages +and close the folder using the Folder.close(true) method. You +cannot expunge without closing the folder. +

    +

    +POP3 does not provide a "received date", so the getReceivedDate +method will return null. +It may be possible to examine other message headers (e.g., the +"Received" headers) to estimate the received date, but these techniques +are error-prone at best. +

    +

    +The POP3 provider supports the POP3 UIDL command, see +{@link com.sun.mail.pop3.POP3Folder#getUID POP3Folder.getUID()}. +You can use it as follows: +

    +
    +if (folder instanceof com.sun.mail.pop3.POP3Folder) {
    +    com.sun.mail.pop3.POP3Folder pf =
    +	(com.sun.mail.pop3.POP3Folder)folder;
    +    String uid = pf.getUID(msg);
    +    if (uid != null)
    +	... // use it
    +}
    +
    +

    +You can also pre-fetch all the UIDs for all messages like this: +

    +
    +FetchProfile fp = new FetchProfile();
    +fp.add(UIDFolder.FetchProfileItem.UID);
    +folder.fetch(folder.getMessages(), fp);
    +
    +

    +Then use the technique above to get the UID for each message. This is +similar to the technique used with the UIDFolder interface supported by +IMAP, but note that POP3 UIDs are strings, not integers like IMAP +UIDs. See the POP3 spec for details. +

    +

    +When the headers of a POP3 message are accessed, the POP3 provider uses +the TOP command to fetch all headers, which are then cached. Use of the +TOP command can be disabled with the mail.pop3.disabletop +property, in which case the entire message content is fetched with the +RETR command. +

    +

    +When the content of a POP3 message is accessed, the POP3 provider uses +the RETR command to fetch the entire message. Normally the message +content is cached in memory. By setting the +mail.pop3.filecache.enable property, the message content +will instead be cached in a temporary file. The file will be removed +when the folder is closed. Caching message content in a file is generally +slower, but uses substantially less memory and may be helpful when dealing +with very large messages. +

    +

    +The {@link com.sun.mail.pop3.POP3Message#invalidate POP3Message.invalidate} +method can be used to invalidate cached data without closing the folder. +Note that if the file cache is being used the data in the file will be +forgotten and fetched from the server if it's needed again, and stored again +in the file cache. +

    +

    +The POP3 CAPA command (defined by +RFC 2449) +will be used to determine the capabilities supported by the server. +Some servers don't implement the CAPA command, and some servers don't +return correct information, so various properties are available to +disable use of certain POP3 commands, including CAPA. +

    +

    +If the server advertises the PIPELINING capability (defined by +RFC 2449), +or the mail.pop3.pipelining property is set, the POP3 +provider will send some commands in batches, which can significantly +improve performance and memory use. +Some servers that don't support the CAPA command or don't advertise +PIPELINING may still support pipelining; experimentation may be required. +

    +

    +If pipelining is supported and the connection is using +SSL, the USER and PASS commands will be sent as a batch. +(If SSL is not being used, the PASS command isn't sent +until the user is verified to avoid exposing the password +if the user name is bad.) +

    +

    +If pipelining is supported, when fetching a message with the RETR command, +the LIST command will be sent as well, and the result will be used to size +the I/O buffer, greatly reducing memory usage when fetching messages. +

    +Properties +

    +The POP3 protocol provider supports the following properties, +which may be set in the JavaMail Session object. +The properties are always set as strings; the Type column describes +how the string is interpreted. For example, use +

    +
    +	props.put("mail.pop3.port", "888");
    +
    +

    +to set the mail.pop3.port property, which is of type int. +

    +

    +Note that if you're using the "pop3s" protocol to access POP3 over SSL, +all the properties would be named "mail.pop3s.*". +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mail.pop3.userStringDefault user name for POP3.
    mail.pop3.hostStringThe POP3 server to connect to.
    mail.pop3.portintThe POP3 server port to connect to, if the connect() method doesn't +explicitly specify one. Defaults to 110.
    mail.pop3.connectiontimeoutintSocket connection timeout value in milliseconds. +This timeout is implemented by java.net.Socket. +Default is infinite timeout.
    mail.pop3.timeoutintSocket read timeout value in milliseconds. +This timeout is implemented by java.net.Socket. +Default is infinite timeout.
    mail.pop3.writetimeoutintSocket write timeout value in milliseconds. +This timeout is implemented by using a +java.util.concurrent.ScheduledExecutorService per connection +that schedules a thread to close the socket if the timeout expires. +Thus, the overhead of using this timeout is one thread per connection. +Default is infinite timeout.
    mail.pop3.rsetbeforequitboolean +Send a POP3 RSET command when closing the folder, before sending the +QUIT command. Useful with POP3 servers that implicitly mark all +messages that are read as "deleted"; this will prevent such messages +from being deleted and expunged unless the client requests so. Default +is false. +
    mail.pop3.message.classString +Class name of a subclass of com.sun.mail.pop3.POP3Message. +The subclass can be used to handle (for example) non-standard +Content-Type headers. The subclass must have a public constructor +of the form MyPOP3Message(Folder f, int msgno) +throws MessagingException. +
    mail.pop3.localaddressString +Local address (host name) to bind to when creating the POP3 socket. +Defaults to the address picked by the Socket class. +Should not normally need to be set, but useful with multi-homed hosts +where it's important to pick a particular local address to bind to. +
    mail.pop3.localportint +Local port number to bind to when creating the POP3 socket. +Defaults to the port number picked by the Socket class. +
    mail.pop3.apop.enableboolean +If set to true, use APOP instead of USER/PASS to login to the +POP3 server, if the POP3 server supports APOP. APOP sends a +digest of the password rather than the clear text password. +Defaults to false. +
    mail.pop3.socketFactorySocketFactory +If set to a class that implements the +javax.net.SocketFactory interface, this class +will be used to create POP3 sockets. Note that this is an +instance of a class, not a name, and must be set using the +put method, not the setProperty method. +
    mail.pop3.socketFactory.classString +If set, specifies the name of a class that implements the +javax.net.SocketFactory interface. This class +will be used to create POP3 sockets. +
    mail.pop3.socketFactory.fallbackboolean +If set to true, failure to create a socket using the specified +socket factory class will cause the socket to be created using +the java.net.Socket class. +Defaults to true. +
    mail.pop3.socketFactory.portint +Specifies the port to connect to when using the specified socket +factory. +If not set, the default port will be used. +
    mail.pop3.ssl.enableboolean +If set to true, use SSL to connect and use the SSL port by default. +Defaults to false for the "pop3" protocol and true for the "pop3s" protocol. +
    mail.pop3.ssl.checkserveridentityboolean +If set to true, check the server identity as specified by +RFC 2595. +These additional checks based on the content of the server's certificate +are intended to prevent man-in-the-middle attacks. +Defaults to false. +
    mail.pop3.ssl.trustString +If set, and a socket factory hasn't been specified, enables use of a +{@link com.sun.mail.util.MailSSLSocketFactory MailSSLSocketFactory}. +If set to "*", all hosts are trusted. +If set to a whitespace separated list of hosts, those hosts are trusted. +Otherwise, trust depends on the certificate the server presents. +
    mail.pop3.ssl.socketFactorySSLSocketFactory +If set to a class that extends the +javax.net.ssl.SSLSocketFactory class, this class +will be used to create POP3 SSL sockets. Note that this is an +instance of a class, not a name, and must be set using the +put method, not the setProperty method. +
    mail.pop3.ssl.socketFactory.classString +If set, specifies the name of a class that extends the +javax.net.ssl.SSLSocketFactory class. This class +will be used to create POP3 SSL sockets. +
    mail.pop3.ssl.socketFactory.portint +Specifies the port to connect to when using the specified socket +factory. +If not set, the default port will be used. +
    mail.pop3.ssl.protocolsstring +Specifies the SSL protocols that will be enabled for SSL connections. +The property value is a whitespace separated list of tokens acceptable +to the javax.net.ssl.SSLSocket.setEnabledProtocols method. +
    mail.pop3.ssl.ciphersuitesstring +Specifies the SSL cipher suites that will be enabled for SSL connections. +The property value is a whitespace separated list of tokens acceptable +to the javax.net.ssl.SSLSocket.setEnabledCipherSuites method. +
    mail.pop3.starttls.enableboolean +If true, enables the use of the STLS command (if +supported by the server) to switch the connection to a TLS-protected +connection before issuing any login commands. +If the server does not support STARTTLS, the connection continues without +the use of TLS; see the +mail.pop3.starttls.required +property to fail if STARTTLS isn't supported. +Note that an appropriate trust store must configured so that the client +will trust the server's certificate. +Defaults to false. +
    mail.pop3.starttls.requiredboolean +If true, requires the use of the STLS command. +If the server doesn't support the STLS command, or the command +fails, the connect method will fail. +Defaults to false. +
    mail.pop3.proxy.hoststring +Specifies the host name of an HTTP web proxy server that will be used for +connections to the mail server. +
    mail.pop3.proxy.portstring +Specifies the port number for the HTTP web proxy server. +Defaults to port 80. +
    mail.pop3.proxy.userstring +Specifies the user name to use to authenticate with the HTTP web proxy server. +By default, no authentication is done. +
    mail.pop3.proxy.passwordstring +Specifies the password to use to authenticate with the HTTP web proxy server. +By default, no authentication is done. +
    mail.pop3.socks.hoststring +Specifies the host name of a SOCKS5 proxy server that will be used for +connections to the mail server. +
    mail.pop3.socks.portstring +Specifies the port number for the SOCKS5 proxy server. +This should only need to be used if the proxy server is not using +the standard port number of 1080. +
    mail.pop3.disabletopboolean +If set to true, the POP3 TOP command will not be used to fetch +message headers. This is useful for POP3 servers that don't +properly implement the TOP command, or that provide incorrect +information in the TOP command results. +Defaults to false. +
    mail.pop3.disablecapaboolean +If set to true, the POP3 CAPA command will not be used to fetch +server capabilities. This is useful for POP3 servers that don't +properly implement the CAPA command, or that provide incorrect +information in the CAPA command results. +Defaults to false. +
    mail.pop3.forgettopheadersboolean +If set to true, the headers that might have been retrieved using +the POP3 TOP command will be forgotten and replaced by headers +retrieved as part of the POP3 RETR command. Some servers, such +as some versions of Microsft Exchange and IBM Lotus Notes, +will return slightly different +headers each time the TOP or RETR command is used. To allow the +POP3 provider to properly parse the message content returned from +the RETR command, the headers also returned by the RETR command +must be used. Setting this property to true will cause these +headers to be used, even if they differ from the headers returned +previously as a result of using the TOP command. +Defaults to false. +
    mail.pop3.filecache.enableboolean +If set to true, the POP3 provider will cache message data in a temporary +file rather than in memory. Messages are only added to the cache when +accessing the message content. Message headers are always cached in +memory (on demand). The file cache is removed when the folder is closed +or the JVM terminates. +Defaults to false. +
    mail.pop3.filecache.dirString +If the file cache is enabled, this property can be used to override the +default directory used by the JDK for temporary files. +
    mail.pop3.cachewritetoboolean +Controls the behavior of the +{@link com.sun.mail.pop3.POP3Message#writeTo writeTo} method +on a POP3 message object. +If set to true, and the message content hasn't yet been cached, +and ignoreList is null, the message is cached before being written. +Otherwise, the message is streamed directly +to the output stream without being cached. +Defaults to false. +
    mail.pop3.keepmessagecontentboolean +The content of a message is cached when it is first fetched. +Normally this cache uses a {@link java.lang.ref.SoftReference SoftReference} +to refer to the cached content. This allows the cached content to be purged +if memory is low, in which case the content will be fetched again if it's +needed. +If this property is set to true, a hard reference to the cached content +will be kept, preventing the memory from being reused until the folder +is closed or the cached content is explicitly invalidated (using the +{@link com.sun.mail.pop3.POP3Message#invalidate invalidate} method). +(This was the behavior in previous versions of JavaMail.) +Defaults to false. +
    mail.pop3.finalizecleancloseboolean +When the finalizer for POP3Store or POP3Folder is called, +should the connection to the server be closed cleanly, as if the +application called the close method? +Or should the connection to the server be closed without sending +any commands to the server? +Defaults to false, the connection is not closed cleanly. +
    +

    +In general, applications should not need to use the classes in this +package directly. Instead, they should use the APIs defined by +javax.mail package (and subpackages). Applications should +never construct instances of POP3Store or +POP3Folder directly. Instead, they should use the +Session method getStore to acquire an +appropriate Store object, and from that acquire +Folder objects. +

    +

    +In addition to printing debugging output as controlled by the +{@link javax.mail.Session Session} configuration, +the com.sun.mail.pop3 provider logs the same information using +{@link java.util.logging.Logger} as described in the following table: +

    + + + + + + + + + + + + + + + + + + + + + + + + +
    Logger NameLogging LevelPurpose
    com.sun.mail.pop3CONFIGConfiguration of the POP3Store
    com.sun.mail.pop3FINEGeneral debugging output
    com.sun.mail.pop3.protocolFINESTComplete protocol trace
    + +

    +WARNING: The APIs unique to this package should be +considered EXPERIMENTAL. They may be changed in the +future in ways that are incompatible with applications using the +current APIs. +

    + + + diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/DigestMD5.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/DigestMD5.java new file mode 100644 index 000000000..184c050d9 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/DigestMD5.java @@ -0,0 +1,255 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import java.io.*; +import java.util.*; +import java.util.logging.Level; +import java.security.*; +import java.nio.charset.StandardCharsets; + +import com.sun.mail.util.MailLogger; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.util.BASE64EncoderStream; +import com.sun.mail.util.BASE64DecoderStream; + +/** + * DIGEST-MD5 authentication support. + * + * @author Dean Gibson + * @author Bill Shannon + */ + +public class DigestMD5 { + + private MailLogger logger; + private MessageDigest md5; + private String uri; + private String clientResponse; + + public DigestMD5(MailLogger logger) { + this.logger = logger.getLogger(this.getClass(), "DEBUG DIGEST-MD5"); + logger.config("DIGEST-MD5 Loaded"); + } + + /** + * Return client's authentication response to server's challenge. + * + * @param host the host name + * @param user the user name + * @param passwd the user's password + * @param realm the security realm + * @param serverChallenge the challenge from the server + * @return byte array with client's response + * @exception IOException for I/O errors + */ + public byte[] authClient(String host, String user, String passwd, + String realm, String serverChallenge) + throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE); + SecureRandom random; + try { + //random = SecureRandom.getInstance("SHA1PRNG"); + random = new SecureRandom(); + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ex) { + logger.log(Level.FINE, "NoSuchAlgorithmException", ex); + throw new IOException(ex.toString()); + } + StringBuilder result = new StringBuilder(); + + uri = "smtp/" + host; + String nc = "00000001"; + String qop = "auth"; + byte[] bytes = new byte[32]; // arbitrary size ... + int resp; + + logger.fine("Begin authentication ..."); + + // Code based on http://www.ietf.org/rfc/rfc2831.txt + Map map = tokenize(serverChallenge); + + if (realm == null) { + String text = map.get("realm"); + realm = text != null ? new StringTokenizer(text, ",").nextToken() + : host; + } + + // server challenge random value + String nonce = map.get("nonce"); + + // Does server support UTF-8 usernames and passwords? + String charset = map.get("charset"); + boolean utf8 = charset != null && charset.equalsIgnoreCase("utf-8"); + + random.nextBytes(bytes); + b64os.write(bytes); + b64os.flush(); + + // client challenge random value + String cnonce = bos.toString("iso-8859-1"); // really ASCII? + bos.reset(); + + // DIGEST-MD5 computation, common portion (order critical) + if (utf8) { + String up = user + ":" + realm + ":" + passwd; + md5.update(md5.digest(up.getBytes(StandardCharsets.UTF_8))); + } else + md5.update(md5.digest( + ASCIIUtility.getBytes(user + ":" + realm + ":" + passwd))); + md5.update(ASCIIUtility.getBytes(":" + nonce + ":" + cnonce)); + clientResponse = toHex(md5.digest()) + + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":"; + + // DIGEST-MD5 computation, client response (order critical) + md5.update(ASCIIUtility.getBytes("AUTHENTICATE:" + uri)); + md5.update(ASCIIUtility.getBytes(clientResponse + toHex(md5.digest()))); + + // build response text (order not critical) + result.append("username=\"" + user + "\""); + result.append(",realm=\"" + realm + "\""); + result.append(",qop=" + qop); + result.append(",nc=" + nc); + result.append(",nonce=\"" + nonce + "\""); + result.append(",cnonce=\"" + cnonce + "\""); + result.append(",digest-uri=\"" + uri + "\""); + if (utf8) + result.append(",charset=\"utf-8\""); + result.append(",response=" + toHex(md5.digest())); + + if (logger.isLoggable(Level.FINE)) + logger.fine("Response => " + result.toString()); + b64os.write(ASCIIUtility.getBytes(result.toString())); + b64os.flush(); + return bos.toByteArray(); + } + + /** + * Allow the client to authenticate the server based on its + * response. + * + * @param serverResponse the response that was received from the server + * @return true if server is authenticated + * @exception IOException for character conversion failures + */ + public boolean authServer(String serverResponse) throws IOException { + Map map = tokenize(serverResponse); + // DIGEST-MD5 computation, server response (order critical) + md5.update(ASCIIUtility.getBytes(":" + uri)); + md5.update(ASCIIUtility.getBytes(clientResponse + toHex(md5.digest()))); + String text = toHex(md5.digest()); + if (!text.equals(map.get("rspauth"))) { + if (logger.isLoggable(Level.FINE)) + logger.fine("Expected => rspauth=" + text); + return false; // server NOT authenticated by client !!! + } + return true; + } + + /** + * Tokenize a response from the server. + * + * @return Map containing key/value pairs from server + */ + @SuppressWarnings("fallthrough") + private Map tokenize(String serverResponse) + throws IOException { + Map map = new HashMap<>(); + byte[] bytes = serverResponse.getBytes("iso-8859-1"); // really ASCII? + String key = null; + int ttype; + StreamTokenizer tokens + = new StreamTokenizer( + new InputStreamReader( + new BASE64DecoderStream( + new ByteArrayInputStream(bytes, 4, bytes.length - 4) + ), "iso-8859-1" // really ASCII? + ) + ); + + tokens.ordinaryChars('0', '9'); // reset digits + tokens.wordChars('0', '9'); // digits may start words + while ((ttype = tokens.nextToken()) != StreamTokenizer.TT_EOF) { + switch (ttype) { + case StreamTokenizer.TT_WORD: + if (key == null) { + key = tokens.sval; + break; + } + // fall-thru + case '"': + if (logger.isLoggable(Level.FINE)) + logger.fine("Received => " + + key + "='" + tokens.sval + "'"); + if (map.containsKey(key)) { // concatenate multiple values + map.put(key, map.get(key) + "," + tokens.sval); + } else { + map.put(key, tokens.sval); + } + key = null; + break; + default: // XXX - should never happen? + break; + } + } + return map; + } + + private static char[] digits = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + /** + * Convert a byte array to a string of hex digits representing the bytes. + */ + private static String toHex(byte[] bytes) { + char[] result = new char[bytes.length * 2]; + + for (int index = 0, i = 0; index < bytes.length; index++) { + int temp = bytes[index] & 0xFF; + result[i++] = digits[temp >> 4]; + result[i++] = digits[temp & 0xF]; + } + return new String(result); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPAddressFailedException.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPAddressFailedException.java new file mode 100644 index 000000000..8d6fe306d --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPAddressFailedException.java @@ -0,0 +1,111 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import javax.mail.SendFailedException; +import javax.mail.internet.InternetAddress; + +/** + * This exception is thrown when the message cannot be sent.

    + * + * The exception includes the address to which the message could not be + * sent. This will usually appear in a chained list of exceptions, + * one per address, attached to a top level SendFailedException that + * aggregates all the addresses. + * + * @since JavaMail 1.3.2 + */ + +public class SMTPAddressFailedException extends SendFailedException { + protected InternetAddress addr; // address that failed + protected String cmd; // command issued to server + protected int rc; // return code from SMTP server + + private static final long serialVersionUID = 804831199768630097L; + + /** + * Constructs an SMTPAddressFailedException with the specified + * address, return code, and error string. + * + * @param addr the address that failed + * @param cmd the command that was sent to the SMTP server + * @param rc the SMTP return code indicating the failure + * @param err the error string from the SMTP server + */ + public SMTPAddressFailedException(InternetAddress addr, String cmd, int rc, + String err) { + super(err); + this.addr = addr; + this.cmd = cmd; + this.rc = rc; + } + + /** + * Return the address that failed. + * + * @return the address + */ + public InternetAddress getAddress() { + return addr; + } + + /** + * Return the command that failed. + * + * @return the command + */ + public String getCommand() { + return cmd; + } + + + /** + * Return the return code from the SMTP server that indicates the + * reason for the failure. See + * RFC 821 + * for interpretation of the return code. + * + * @return the return code + */ + public int getReturnCode() { + return rc; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPAddressSucceededException.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPAddressSucceededException.java new file mode 100644 index 000000000..3f40a8cda --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPAddressSucceededException.java @@ -0,0 +1,110 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import javax.mail.MessagingException; +import javax.mail.internet.InternetAddress; + +/** + * This exception is chained off a SendFailedException when the + * mail.smtp.reportsuccess property is true. It + * indicates an address to which the message was sent. The command + * will be an SMTP RCPT command and the return code will be the + * return code from that command. + * + * @since JavaMail 1.3.2 + */ + +public class SMTPAddressSucceededException extends MessagingException { + protected InternetAddress addr; // address that succeeded + protected String cmd; // command issued to server + protected int rc; // return code from SMTP server + + private static final long serialVersionUID = -1168335848623096749L; + + /** + * Constructs an SMTPAddressSucceededException with the specified + * address, return code, and error string. + * + * @param addr the address that succeeded + * @param cmd the command that was sent to the SMTP server + * @param rc the SMTP return code indicating the success + * @param err the error string from the SMTP server + */ + public SMTPAddressSucceededException(InternetAddress addr, + String cmd, int rc, String err) { + super(err); + this.addr = addr; + this.cmd = cmd; + this.rc = rc; + } + + /** + * Return the address that succeeded. + * + * @return the address + */ + public InternetAddress getAddress() { + return addr; + } + + /** + * Return the command that succeeded. + * + * @return the command + */ + public String getCommand() { + return cmd; + } + + + /** + * Return the return code from the SMTP server that indicates the + * reason for the success. See + * RFC 821 + * for interpretation of the return code. + * + * @return the return code + */ + public int getReturnCode() { + return rc; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPMessage.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPMessage.java new file mode 100644 index 000000000..83fd3b5ba --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPMessage.java @@ -0,0 +1,345 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import java.io.*; +import javax.mail.*; +import javax.mail.internet.*; + +/** + * This class is a specialization of the MimeMessage class that allows + * you to specify various SMTP options and parameters that will be + * used when this message is sent over SMTP. Simply use this class + * instead of MimeMessage and set SMTP options using the methods on + * this class.

    + * + * See the com.sun.mail.smtp package + * documentation for further information on the SMTP protocol provider.

    + * + * @author Bill Shannon + * @see javax.mail.internet.MimeMessage + */ + +public class SMTPMessage extends MimeMessage { + + /** Never notify of delivery status */ + public static final int NOTIFY_NEVER = -1; + /** Notify of delivery success */ + public static final int NOTIFY_SUCCESS = 1; + /** Notify of delivery failure */ + public static final int NOTIFY_FAILURE = 2; + /** Notify of delivery delay */ + public static final int NOTIFY_DELAY = 4; + + /** Return full message with delivery status notification */ + public static final int RETURN_FULL = 1; + /** Return only message headers with delivery status notification */ + public static final int RETURN_HDRS = 2; + + private static final String[] returnOptionString = { null, "FULL", "HDRS" }; + + private String envelopeFrom; // the string to use in the MAIL FROM: command + private int notifyOptions = 0; + private int returnOption = 0; + private boolean sendPartial = false; + private boolean allow8bitMIME = false; + private String submitter = null; // RFC 2554 AUTH=submitter + private String extension = null; // extensions to use with MAIL command + + /** + * Default constructor. An empty message object is created. + * The headers field is set to an empty InternetHeaders + * object. The flags field is set to an empty Flags + * object. The modified flag is set to true. + * + * @param session the Session + */ + public SMTPMessage(Session session) { + super(session); + } + + /** + * Constructs an SMTPMessage by reading and parsing the data from the + * specified MIME InputStream. The InputStream will be left positioned + * at the end of the data for the message. Note that the input stream + * parse is done within this constructor itself. + * + * @param session Session object for this message + * @param is the message input stream + * @exception MessagingException for failures + */ + public SMTPMessage(Session session, InputStream is) + throws MessagingException { + super(session, is); + } + + /** + * Constructs a new SMTPMessage with content initialized from the + * source MimeMessage. The new message is independent + * of the original.

    + * + * Note: The current implementation is rather inefficient, copying + * the data more times than strictly necessary. + * + * @param source the message to copy content from + * @exception MessagingException for failures + */ + public SMTPMessage(MimeMessage source) throws MessagingException { + super(source); + } + + /** + * Set the From address to appear in the SMTP envelope. Note that this + * is different than the From address that appears in the message itself. + * The envelope From address is typically used when reporting errors. + * See RFC 821 for + * details.

    + * + * If set, overrides the mail.smtp.from property. + * + * @param from the envelope From address + */ + public void setEnvelopeFrom(String from) { + envelopeFrom = from; + } + + /** + * Return the envelope From address. + * + * @return the envelope From address, or null if not set + */ + public String getEnvelopeFrom() { + return envelopeFrom; + } + + /** + * Set notification options to be used if the server supports + * Delivery Status Notification + * (RFC 1891). + * Either NOTIFY_NEVER or some combination of + * NOTIFY_SUCCESS, NOTIFY_FAILURE, and + * NOTIFY_DELAY.

    + * + * If set, overrides the mail.smtp.dsn.notify property. + * + * @param options notification options + */ + public void setNotifyOptions(int options) { + if (options < -1 || options >= 8) + throw new IllegalArgumentException("Bad return option"); + notifyOptions = options; + } + + /** + * Get notification options. Returns zero if no options set. + * + * @return notification options + */ + public int getNotifyOptions() { + return notifyOptions; + } + + /** + * Return notification options as an RFC 1891 string. + * Returns null if no options set. + */ + String getDSNNotify() { + if (notifyOptions == 0) + return null; + if (notifyOptions == NOTIFY_NEVER) + return "NEVER"; + StringBuilder sb = new StringBuilder(); + if ((notifyOptions & NOTIFY_SUCCESS) != 0) + sb.append("SUCCESS"); + if ((notifyOptions & NOTIFY_FAILURE) != 0) { + if (sb.length() != 0) + sb.append(','); + sb.append("FAILURE"); + } + if ((notifyOptions & NOTIFY_DELAY) != 0) { + if (sb.length() != 0) + sb.append(','); + sb.append("DELAY"); + } + return sb.toString(); + } + + /** + * Set return option to be used if server supports + * Delivery Status Notification + * (RFC 1891). + * Either RETURN_FULL or RETURN_HDRS.

    + * + * If set, overrides the mail.smtp.dsn.ret property. + * + * @param option return option + */ + public void setReturnOption(int option) { + if (option < 0 || option > RETURN_HDRS) + throw new IllegalArgumentException("Bad return option"); + returnOption = option; + } + + /** + * Return return option. Returns zero if no option set. + * + * @return return option + */ + public int getReturnOption() { + return returnOption; + } + + /** + * Return return option as an RFC 1891 string. + * Returns null if no option set. + */ + String getDSNRet() { + return returnOptionString[returnOption]; + } + + /** + * If set to true, and the server supports the 8BITMIME extension, text + * parts of this message that use the "quoted-printable" or "base64" + * encodings are converted to use "8bit" encoding if they follow the + * RFC 2045 rules for 8bit text.

    + * + * If true, overrides the mail.smtp.allow8bitmime property. + * + * @param allow allow 8-bit flag + */ + public void setAllow8bitMIME(boolean allow) { + allow8bitMIME = allow; + } + + /** + * Is use of the 8BITMIME extension is allowed? + * + * @return allow 8-bit flag + */ + public boolean getAllow8bitMIME() { + return allow8bitMIME; + } + + /** + * If set to true, and this message has some valid and some invalid + * addresses, send the message anyway, reporting the partial failure with + * a SendFailedException. If set to false (the default), the message is + * not sent to any of the recipients if there is an invalid recipient + * address.

    + * + * If true, overrides the mail.smtp.sendpartial property. + * + * @param partial send partial flag + */ + public void setSendPartial(boolean partial) { + sendPartial = partial; + } + + /** + * Send message if some addresses are invalid? + * + * @return send partial flag + */ + public boolean getSendPartial() { + return sendPartial; + } + + /** + * Gets the submitter to be used for the RFC 2554 AUTH= value + * in the MAIL FROM command. + * + * @return the name of the submitter. + */ + public String getSubmitter() { + return submitter; + } + + /** + * Sets the submitter to be used for the RFC 2554 AUTH= value + * in the MAIL FROM command. Normally only used by a server + * that's relaying a message. Clients will typically not + * set a submitter. See + * RFC 2554 + * for details. + * + * @param submitter the name of the submitter + */ + public void setSubmitter(String submitter) { + this.submitter = submitter; + } + + /** + * Gets the extension string to use with the MAIL command. + * + * @return the extension string + * + * @since JavaMail 1.3.2 + */ + public String getMailExtension() { + return extension; + } + + /** + * Set the extension string to use with the MAIL command. + * The extension string can be used to specify standard SMTP + * service extensions as well as vendor-specific extensions. + * Typically the application should use the + * {@link com.sun.mail.smtp.SMTPTransport SMTPTransport} + * method {@link com.sun.mail.smtp.SMTPTransport#supportsExtension + * supportsExtension} + * to verify that the server supports the desired service extension. + * See RFC 1869 + * and other RFCs that define specific extensions.

    + * + * For example: + * + *

    +     * if (smtpTransport.supportsExtension("DELIVERBY"))
    +     *    smtpMsg.setMailExtension("BY=60;R");
    +     * 
    + * + * @param extension the extension string + * @since JavaMail 1.3.2 + */ + public void setMailExtension(String extension) { + this.extension = extension; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPOutputStream.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPOutputStream.java new file mode 100644 index 000000000..72fd38089 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPOutputStream.java @@ -0,0 +1,119 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import java.io.*; +import com.sun.mail.util.CRLFOutputStream; + +/** + * In addition to converting lines into the canonical format, + * i.e., terminating lines with the CRLF sequence, escapes the "." + * by adding another "." to any "." that appears in the beginning + * of a line. See RFC821 section 4.5.2. + * + * @author Max Spivak + * @see CRLFOutputStream + */ +public class SMTPOutputStream extends CRLFOutputStream { + public SMTPOutputStream(OutputStream os) { + super(os); + } + + @Override + public void write(int b) throws IOException { + // if that last character was a newline, and the current + // character is ".", we always write out an extra ".". + if ((lastb == '\n' || lastb == '\r' || lastb == -1) && b == '.') { + out.write('.'); + } + + super.write(b); + } + + /* + * This method has been added to improve performance. + */ + @Override + public void write(byte b[], int off, int len) throws IOException { + int lastc = (lastb == -1) ? '\n' : lastb; + int start = off; + + len += off; + for (int i = off; i < len; i++) { + if ((lastc == '\n' || lastc == '\r') && b[i] == '.') { + super.write(b, start, i - start); + out.write('.'); + start = i; + } + lastc = b[i]; + } + if ((len - start) > 0) + super.write(b, start, len - start); + } + + /** + * Override flush method in FilterOutputStream. + * + * The MimeMessage writeTo method flushes its buffer at the end, + * but we don't want to flush data out to the socket until we've + * also written the terminating "\r\n.\r\n". + * + * We buffer nothing so there's nothing to flush. We depend + * on the fact that CRLFOutputStream also buffers nothing. + * SMTPTransport will manually flush the socket before reading + * the response. + */ + @Override + public void flush() { + // do nothing + } + + /** + * Ensure we're at the beginning of a line. + * Write CRLF if not. + * + * @exception IOException if the write fails + */ + public void ensureAtBOL() throws IOException { + if (!atBOL) + super.writeln(); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPProvider.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPProvider.java new file mode 100644 index 000000000..8e752eca0 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPProvider.java @@ -0,0 +1,54 @@ + +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import javax.mail.Provider; + +/** + * The SMTP protocol provider. + */ +public class SMTPProvider extends Provider { + public SMTPProvider() { + super(Provider.Type.TRANSPORT, "smtp", SMTPTransport.class.getName(), + "Oracle", null); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSSLProvider.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSSLProvider.java new file mode 100644 index 000000000..a0ee09bb0 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSSLProvider.java @@ -0,0 +1,54 @@ + +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import javax.mail.Provider; + +/** + * The SMTP SSL protocol provider. + */ +public class SMTPSSLProvider extends Provider { + public SMTPSSLProvider() { + super(Provider.Type.TRANSPORT, "smtps", + SMTPSSLTransport.class.getName(), "Oracle", null); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSSLTransport.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSSLTransport.java new file mode 100644 index 000000000..06ad2c836 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSSLTransport.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import javax.mail.*; + +/** + * This class implements the Transport abstract class using SMTP + * over SSL for message submission and transport. + * + * @author Bill Shannon + */ + +public class SMTPSSLTransport extends SMTPTransport { + + /** + * Constructor. + * + * @param session the Session + * @param urlname the URLName of this transport + */ + public SMTPSSLTransport(Session session, URLName urlname) { + super(session, urlname, "smtps", true); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSaslAuthenticator.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSaslAuthenticator.java new file mode 100644 index 000000000..9774d0452 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSaslAuthenticator.java @@ -0,0 +1,252 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import java.io.*; +import java.util.*; +import java.util.logging.Level; +import javax.security.sasl.*; +import javax.security.auth.callback.*; +import javax.mail.MessagingException; + +import com.sun.mail.util.MailLogger; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.util.BASE64EncoderStream; +import com.sun.mail.util.BASE64DecoderStream; + +/** + * This class contains a single method that does authentication using + * SASL. This is in a separate class so that it can be compiled with + * J2SE 1.5. Eventually it should be merged into SMTPTransport.java. + */ + +public class SMTPSaslAuthenticator implements SaslAuthenticator { + + private SMTPTransport pr; + private String name; + private Properties props; + private MailLogger logger; + private String host; + + /* + * This is a hack to initialize the OAUTH SASL provider just before, + * and only if, we might need it. This avoids the need for the user + * to initialize it explicitly, or manually configure the security + * providers file. + */ + static { + try { + com.sun.mail.auth.OAuth2SaslClientFactory.init(); + } catch (Throwable t) { } + } + + public SMTPSaslAuthenticator(SMTPTransport pr, String name, + Properties props, MailLogger logger, String host) { + this.pr = pr; + this.name = name; + this.props = props; + this.logger = logger; + this.host = host; + } + + @Override + public boolean authenticate(String[] mechs, final String realm, + final String authzid, final String u, + final String p) throws MessagingException { + + boolean done = false; + if (logger.isLoggable(Level.FINE)) { + logger.fine("SASL Mechanisms:"); + for (int i = 0; i < mechs.length; i++) + logger.fine(" " + mechs[i]); + logger.fine(""); + } + + SaslClient sc; + CallbackHandler cbh = new CallbackHandler() { + @Override + public void handle(Callback[] callbacks) { + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL callback length: " + callbacks.length); + for (int i = 0; i < callbacks.length; i++) { + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL callback " + i + ": " + callbacks[i]); + if (callbacks[i] instanceof NameCallback) { + NameCallback ncb = (NameCallback)callbacks[i]; + ncb.setName(u); + } else if (callbacks[i] instanceof PasswordCallback) { + PasswordCallback pcb = (PasswordCallback)callbacks[i]; + pcb.setPassword(p.toCharArray()); + } else if (callbacks[i] instanceof RealmCallback) { + RealmCallback rcb = (RealmCallback)callbacks[i]; + rcb.setText(realm != null ? + realm : rcb.getDefaultText()); + } else if (callbacks[i] instanceof RealmChoiceCallback) { + RealmChoiceCallback rcb = + (RealmChoiceCallback)callbacks[i]; + if (realm == null) + rcb.setSelectedIndex(rcb.getDefaultChoice()); + else { + // need to find specified realm in list + String[] choices = rcb.getChoices(); + for (int k = 0; k < choices.length; k++) { + if (choices[k].equals(realm)) { + rcb.setSelectedIndex(k); + break; + } + } + } + } + } + } + }; + + try { + @SuppressWarnings("unchecked") + Map propsMap = (Map) props; + sc = Sasl.createSaslClient(mechs, authzid, name, host, + propsMap, cbh); + } catch (SaslException sex) { + logger.log(Level.FINE, "Failed to create SASL client", sex); + throw new UnsupportedOperationException(sex.getMessage(), sex); + } + if (sc == null) { + logger.fine("No SASL support"); + throw new UnsupportedOperationException("No SASL support"); + } + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL client " + sc.getMechanismName()); + + int resp; + try { + String mech = sc.getMechanismName(); + String ir = null; + if (sc.hasInitialResponse()) { + byte[] ba = sc.evaluateChallenge(new byte[0]); + if (ba.length > 0) { + ba = BASE64EncoderStream.encode(ba); + ir = ASCIIUtility.toString(ba, 0, ba.length); + } else + ir = "="; + } + if (ir != null) + resp = pr.simpleCommand("AUTH " + mech + " " + ir); + else + resp = pr.simpleCommand("AUTH " + mech); + + /* + * A 530 response indicates that the server wants us to + * issue a STARTTLS command first. Do that and try again. + */ + if (resp == 530) { + pr.startTLS(); + if (ir != null) + resp = pr.simpleCommand("AUTH " + mech + " " + ir); + else + resp = pr.simpleCommand("AUTH " + mech); + } + + if (resp == 235) + return true; // success already! + + if (resp != 334) + return false; + } catch (Exception ex) { + logger.log(Level.FINE, "SASL AUTHENTICATE Exception", ex); + return false; + } + + while (!done) { // loop till we are done + try { + if (resp == 334) { + byte[] ba = null; + if (!sc.isComplete()) { + ba = ASCIIUtility.getBytes(responseText(pr)); + if (ba.length > 0) + ba = BASE64DecoderStream.decode(ba); + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL challenge: " + + ASCIIUtility.toString(ba, 0, ba.length) + " :"); + ba = sc.evaluateChallenge(ba); + } + if (ba == null) { + logger.fine("SASL: no response"); + resp = pr.simpleCommand(""); + } else { + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL response: " + + ASCIIUtility.toString(ba, 0, ba.length) + " :"); + ba = BASE64EncoderStream.encode(ba); + resp = pr.simpleCommand(ba); + } + } else + done = true; + } catch (Exception ioex) { + logger.log(Level.FINE, "SASL Exception", ioex); + done = true; + // XXX - ultimately return true??? + } + } + if (resp != 235) + return false; + + if (sc.isComplete() /*&& res.status == SUCCESS*/) { + String qop = (String)sc.getNegotiatedProperty(Sasl.QOP); + if (qop != null && (qop.equalsIgnoreCase("auth-int") || + qop.equalsIgnoreCase("auth-conf"))) { + // XXX - NOT SUPPORTED!!! + logger.fine( + "SASL Mechanism requires integrity or confidentiality"); + return false; + } + } + + return true; + } + + private static final String responseText(SMTPTransport pr) { + String resp = pr.getLastServerResponse().trim(); + if (resp.length() > 4) + return resp.substring(4); + else + return ""; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSendFailedException.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSendFailedException.java new file mode 100644 index 000000000..95dc38917 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSendFailedException.java @@ -0,0 +1,106 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import javax.mail.Address; +import javax.mail.SendFailedException; +import javax.mail.internet.InternetAddress; + +/** + * This exception is thrown when the message cannot be sent.

    + * + * This exception will usually appear first in a chained list of exceptions, + * followed by SMTPAddressFailedExceptions and/or + * SMTPAddressSucceededExceptions, * one per address. + * This exception corresponds to one of the SMTP commands used to + * send a message, such as the MAIL, DATA, and "end of data" commands, + * but not including the RCPT command. + * + * @since JavaMail 1.3.2 + */ + +public class SMTPSendFailedException extends SendFailedException { + protected InternetAddress addr; // address that failed + protected String cmd; // command issued to server + protected int rc; // return code from SMTP server + + private static final long serialVersionUID = 8049122628728932894L; + + /** + * Constructs an SMTPSendFailedException with the specified + * address, return code, and error string. + * + * @param cmd the command that was sent to the SMTP server + * @param rc the SMTP return code indicating the failure + * @param err the error string from the SMTP server + * @param ex a chained exception + * @param vs the valid addresses the message was sent to + * @param vus the valid addresses the message was not sent to + * @param inv the invalid addresses + */ + public SMTPSendFailedException(String cmd, int rc, String err, Exception ex, + Address[] vs, Address[] vus, Address[] inv) { + super(err, ex, vs, vus, inv); + this.cmd = cmd; + this.rc = rc; + } + + /** + * Return the command that failed. + * + * @return the command + */ + public String getCommand() { + return cmd; + } + + /** + * Return the return code from the SMTP server that indicates the + * reason for the failure. See + * RFC 821 + * for interpretation of the return code. + * + * @return the return code + */ + public int getReturnCode() { + return rc; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSenderFailedException.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSenderFailedException.java new file mode 100644 index 000000000..17ef08742 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPSenderFailedException.java @@ -0,0 +1,109 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import javax.mail.SendFailedException; +import javax.mail.internet.InternetAddress; + +/** + * This exception is thrown when the message cannot be sent.

    + * + * The exception includes the sender's address, which the mail server + * rejected. + * + * @since JavaMail 1.4.4 + */ + +public class SMTPSenderFailedException extends SendFailedException { + protected InternetAddress addr; // address that failed + protected String cmd; // command issued to server + protected int rc; // return code from SMTP server + + private static final long serialVersionUID = 514540454964476947L; + + /** + * Constructs an SMTPSenderFailedException with the specified + * address, return code, and error string. + * + * @param addr the address that failed + * @param cmd the command that was sent to the SMTP server + * @param rc the SMTP return code indicating the failure + * @param err the error string from the SMTP server + */ + public SMTPSenderFailedException(InternetAddress addr, String cmd, int rc, + String err) { + super(err); + this.addr = addr; + this.cmd = cmd; + this.rc = rc; + } + + /** + * Return the address that failed. + * + * @return the address + */ + public InternetAddress getAddress() { + return addr; + } + + /** + * Return the command that failed. + * + * @return the command + */ + public String getCommand() { + return cmd; + } + + + /** + * Return the return code from the SMTP server that indicates the + * reason for the failure. See + * RFC 821 + * for interpretation of the return code. + * + * @return the return code + */ + public int getReturnCode() { + return rc; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPTransport.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPTransport.java new file mode 100644 index 000000000..9f3927862 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SMTPTransport.java @@ -0,0 +1,2815 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.logging.Level; +import java.lang.reflect.*; +import java.nio.charset.StandardCharsets; +import javax.net.ssl.SSLSocket; + +import javax.mail.*; +import javax.mail.event.*; +import javax.mail.internet.*; + +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.MailLogger; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.util.SocketFetcher; +import com.sun.mail.util.MailConnectException; +import com.sun.mail.util.SocketConnectException; +import com.sun.mail.util.BASE64EncoderStream; +import com.sun.mail.util.LineInputStream; +import com.sun.mail.util.TraceInputStream; +import com.sun.mail.util.TraceOutputStream; +import com.sun.mail.auth.Ntlm; + +/** + * This class implements the Transport abstract class using SMTP for + * message submission and transport.

    + * + * See the com.sun.mail.smtp package + * documentation for further information on the SMTP protocol provider.

    + * + * This class includes many protected methods that allow a subclass to + * extend this class and add support for non-standard SMTP commands. + * The {@link #issueCommand} and {@link #sendCommand} methods can be + * used to send simple SMTP commands. Other methods such as the + * {@link #mailFrom} and {@link #data} methods can be overridden to + * insert new commands before or after the corresponding SMTP commands. + * For example, a subclass could do this to send the XACT command + * before sending the DATA command: + *

    + *	protected OutputStream data() throws MessagingException {
    + *	    if (supportsExtension("XACCOUNTING"))
    + *	        issueCommand("XACT", 25);
    + *	    return super.data();
    + *	}
    + * 
    + * + * @author Max Spivak + * @author Bill Shannon + * @author Dean Gibson (DIGEST-MD5 authentication) + * @author Lu\u00EDs Serralheiro (NTLM authentication) + * + * @see javax.mail.event.ConnectionEvent + * @see javax.mail.event.TransportEvent + */ + +public class SMTPTransport extends Transport { + + private String name = "smtp"; // Name of this protocol + private int defaultPort = 25; // default SMTP port + private boolean isSSL = false; // use SSL? + private String host; // host we're connected to + + // Following fields valid only during the sendMessage method. + private MimeMessage message; // Message to be sent + private Address[] addresses; // Addresses to which to send the msg + // Valid sent, valid unsent and invalid addresses + private Address[] validSentAddr, validUnsentAddr, invalidAddr; + // Did we send the message even though some addresses were invalid? + private boolean sendPartiallyFailed = false; + // If so, here's an exception we need to throw + private MessagingException exception; + // stream where message data is written + private SMTPOutputStream dataStream; + + // Map of SMTP service extensions supported by server, if EHLO used. + private Hashtable extMap; + + private Map authenticators + = new HashMap<>(); + private String defaultAuthenticationMechanisms; // set in constructor + + private boolean quitWait = false; // true if we should wait + + private String saslRealm = UNKNOWN; + private String authorizationID = UNKNOWN; + private boolean enableSASL = false; // enable SASL authentication + private boolean useCanonicalHostName = false; // use canonical host name? + private String[] saslMechanisms = UNKNOWN_SA; + + private String ntlmDomain = UNKNOWN; // for ntlm authentication + + private boolean reportSuccess; // throw an exception even on success + private boolean useStartTLS; // use STARTTLS command + private boolean requireStartTLS; // require STARTTLS command + private boolean useRset; // use RSET instead of NOOP + private boolean noopStrict = true; // NOOP must return 250 for success + + private MailLogger logger; // debug logger + private MailLogger traceLogger; // protocol trace logger + private String localHostName; // our own host name + private String lastServerResponse; // last SMTP response + private int lastReturnCode; // last SMTP return code + private boolean notificationDone; // only notify once per send + + private SaslAuthenticator saslAuthenticator; // if SASL is being used + + private boolean noauthdebug = true; // hide auth info in debug output + private boolean debugusername; // include username in debug output? + private boolean debugpassword; // include password in debug output? + private boolean allowutf8; // allow UTF-8 usernames and passwords? + private int chunkSize; // chunk size if CHUNKING supported + + /** Headers that should not be included when sending */ + private static final String[] ignoreList = { "Bcc", "Content-Length" }; + private static final byte[] CRLF = { (byte)'\r', (byte)'\n' }; + private static final String UNKNOWN = "UNKNOWN"; // place holder + private static final String[] UNKNOWN_SA = new String[0]; // place holder + + /** + * Constructor that takes a Session object and a URLName + * that represents a specific SMTP server. + * + * @param session the Session + * @param urlname the URLName of this transport + */ + public SMTPTransport(Session session, URLName urlname) { + this(session, urlname, "smtp", false); + } + + /** + * Constructor used by this class and by SMTPSSLTransport subclass. + * + * @param session the Session + * @param urlname the URLName of this transport + * @param name the protocol name of this transport + * @param isSSL use SSL to connect? + */ + protected SMTPTransport(Session session, URLName urlname, + String name, boolean isSSL) { + super(session, urlname); + Properties props = session.getProperties(); + + logger = new MailLogger(this.getClass(), "DEBUG SMTP", + session.getDebug(), session.getDebugOut()); + traceLogger = logger.getSubLogger("protocol", null); + noauthdebug = !PropUtil.getBooleanProperty(props, + "mail.debug.auth", false); + debugusername = PropUtil.getBooleanProperty(props, + "mail.debug.auth.username", true); + debugpassword = PropUtil.getBooleanProperty(props, + "mail.debug.auth.password", false); + if (urlname != null) + name = urlname.getProtocol(); + this.name = name; + if (!isSSL) + isSSL = PropUtil.getBooleanProperty(props, + "mail." + name + ".ssl.enable", false); + if (isSSL) + this.defaultPort = 465; + else + this.defaultPort = 25; + this.isSSL = isSSL; + + // setting mail.smtp.quitwait to false causes us to not wait for the + // response from the QUIT command + quitWait = PropUtil.getBooleanProperty(props, + "mail." + name + ".quitwait", true); + + // mail.smtp.reportsuccess causes us to throw an exception on success + reportSuccess = PropUtil.getBooleanProperty(props, + "mail." + name + ".reportsuccess", false); + + // mail.smtp.starttls.enable enables use of STARTTLS command + useStartTLS = PropUtil.getBooleanProperty(props, + "mail." + name + ".starttls.enable", false); + + // mail.smtp.starttls.required requires use of STARTTLS command + requireStartTLS = PropUtil.getBooleanProperty(props, + "mail." + name + ".starttls.required", false); + + // mail.smtp.userset causes us to use RSET instead of NOOP + // for isConnected + useRset = PropUtil.getBooleanProperty(props, + "mail." + name + ".userset", false); + + // mail.smtp.noop.strict requires 250 response to indicate success + noopStrict = PropUtil.getBooleanProperty(props, + "mail." + name + ".noop.strict", true); + + // check if SASL is enabled + enableSASL = PropUtil.getBooleanProperty(props, + "mail." + name + ".sasl.enable", false); + if (enableSASL) + logger.config("enable SASL"); + useCanonicalHostName = PropUtil.getBooleanProperty(props, + "mail." + name + ".sasl.usecanonicalhostname", false); + if (useCanonicalHostName) + logger.config("use canonical host name"); + + allowutf8 = PropUtil.getBooleanProperty(props, + "mail.mime.allowutf8", false); + if (allowutf8) + logger.config("allow UTF-8"); + + chunkSize = PropUtil.getIntProperty(props, + "mail." + name + ".chunksize", -1); + if (chunkSize > 0 && logger.isLoggable(Level.CONFIG)) + logger.config("chunk size " + chunkSize); + + // created here, because they're inner classes that reference "this" + Authenticator[] a = new Authenticator[] { + new LoginAuthenticator(), + new PlainAuthenticator(), + new DigestMD5Authenticator(), + new NtlmAuthenticator(), + new OAuth2Authenticator() + }; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < a.length; i++) { + authenticators.put(a[i].getMechanism(), a[i]); + sb.append(a[i].getMechanism()).append(' '); + } + defaultAuthenticationMechanisms = sb.toString(); + } + + /** + * Get the name of the local host, for use in the EHLO and HELO commands. + * The property mail.smtp.localhost overrides mail.smtp.localaddress, + * which overrides what InetAddress would tell us. + * + * @return the local host name + */ + public synchronized String getLocalHost() { + // get our hostname and cache it for future use + if (localHostName == null || localHostName.length() <= 0) + localHostName = + session.getProperty("mail." + name + ".localhost"); + if (localHostName == null || localHostName.length() <= 0) + localHostName = + session.getProperty("mail." + name + ".localaddress"); + try { + if (localHostName == null || localHostName.length() <= 0) { + InetAddress localHost = InetAddress.getLocalHost(); + localHostName = localHost.getCanonicalHostName(); + // if we can't get our name, use local address literal + if (localHostName == null) + // XXX - not correct for IPv6 + localHostName = "[" + localHost.getHostAddress() + "]"; + } + } catch (UnknownHostException uhex) { + } + + // last chance, try to get our address from our socket + if (localHostName == null || localHostName.length() <= 0) { + if (serverSocket != null && serverSocket.isBound()) { + InetAddress localHost = serverSocket.getLocalAddress(); + localHostName = localHost.getCanonicalHostName(); + // if we can't get our name, use local address literal + if (localHostName == null) + // XXX - not correct for IPv6 + localHostName = "[" + localHost.getHostAddress() + "]"; + } + } + return localHostName; + } + + /** + * Set the name of the local host, for use in the EHLO and HELO commands. + * + * @param localhost the local host name + * @since JavaMail 1.3.1 + */ + public synchronized void setLocalHost(String localhost) { + localHostName = localhost; + } + + /** + * Start the SMTP protocol on the given socket, which was already + * connected by the caller. Useful for implementing the SMTP ATRN + * command (RFC 2645) where an existing connection is used when + * the server reverses roles and becomes the client. + * + * @param socket the already connected socket + * @exception MessagingException for failures + * @since JavaMail 1.3.3 + */ + public synchronized void connect(Socket socket) throws MessagingException { + serverSocket = socket; + super.connect(); + } + + /** + * Gets the authorization ID to be used for authentication. + * + * @return the authorization ID to use for authentication. + * + * @since JavaMail 1.4.4 + */ + public synchronized String getAuthorizationId() { + if (authorizationID == UNKNOWN) { + authorizationID = + session.getProperty("mail." + name + ".sasl.authorizationid"); + } + return authorizationID; + } + + /** + * Sets the authorization ID to be used for authentication. + * + * @param authzid the authorization ID to use for + * authentication. + * + * @since JavaMail 1.4.4 + */ + public synchronized void setAuthorizationID(String authzid) { + this.authorizationID = authzid; + } + + /** + * Is SASL authentication enabled? + * + * @return true if SASL authentication is enabled + * + * @since JavaMail 1.4.4 + */ + public synchronized boolean getSASLEnabled() { + return enableSASL; + } + + /** + * Set whether SASL authentication is enabled. + * + * @param enableSASL should we enable SASL authentication? + * + * @since JavaMail 1.4.4 + */ + public synchronized void setSASLEnabled(boolean enableSASL) { + this.enableSASL = enableSASL; + } + + /** + * Gets the SASL realm to be used for DIGEST-MD5 authentication. + * + * @return the name of the realm to use for SASL authentication. + * + * @since JavaMail 1.3.1 + */ + public synchronized String getSASLRealm() { + if (saslRealm == UNKNOWN) { + saslRealm = session.getProperty("mail." + name + ".sasl.realm"); + if (saslRealm == null) // try old name + saslRealm = session.getProperty("mail." + name + ".saslrealm"); + } + return saslRealm; + } + + /** + * Sets the SASL realm to be used for DIGEST-MD5 authentication. + * + * @param saslRealm the name of the realm to use for + * SASL authentication. + * + * @since JavaMail 1.3.1 + */ + public synchronized void setSASLRealm(String saslRealm) { + this.saslRealm = saslRealm; + } + + /** + * Should SASL use the canonical host name? + * + * @return true if SASL should use the canonical host name + * + * @since JavaMail 1.5.2 + */ + public synchronized boolean getUseCanonicalHostName() { + return useCanonicalHostName; + } + + /** + * Set whether SASL should use the canonical host name. + * + * @param useCanonicalHostName should SASL use the canonical host name? + * + * @since JavaMail 1.5.2 + */ + public synchronized void setUseCanonicalHostName( + boolean useCanonicalHostName) { + this.useCanonicalHostName = useCanonicalHostName; + } + + /** + * Get the list of SASL mechanisms to consider if SASL authentication + * is enabled. If the list is empty or null, all available SASL mechanisms + * are considered. + * + * @return the array of SASL mechanisms to consider + * + * @since JavaMail 1.4.4 + */ + public synchronized String[] getSASLMechanisms() { + if (saslMechanisms == UNKNOWN_SA) { + List v = new ArrayList<>(5); + String s = session.getProperty("mail." + name + ".sasl.mechanisms"); + if (s != null && s.length() > 0) { + if (logger.isLoggable(Level.FINE)) + logger.fine("SASL mechanisms allowed: " + s); + StringTokenizer st = new StringTokenizer(s, " ,"); + while (st.hasMoreTokens()) { + String m = st.nextToken(); + if (m.length() > 0) + v.add(m); + } + } + saslMechanisms = new String[v.size()]; + v.toArray(saslMechanisms); + } + if (saslMechanisms == null) + return null; + return saslMechanisms.clone(); + } + + /** + * Set the list of SASL mechanisms to consider if SASL authentication + * is enabled. If the list is empty or null, all available SASL mechanisms + * are considered. + * + * @param mechanisms the array of SASL mechanisms to consider + * + * @since JavaMail 1.4.4 + */ + public synchronized void setSASLMechanisms(String[] mechanisms) { + if (mechanisms != null) + mechanisms = mechanisms.clone(); + this.saslMechanisms = mechanisms; + } + + /** + * Gets the NTLM domain to be used for NTLM authentication. + * + * @return the name of the domain to use for NTLM authentication. + * + * @since JavaMail 1.4.3 + */ + public synchronized String getNTLMDomain() { + if (ntlmDomain == UNKNOWN) { + ntlmDomain = + session.getProperty("mail." + name + ".auth.ntlm.domain"); + } + return ntlmDomain; + } + + /** + * Sets the NTLM domain to be used for NTLM authentication. + * + * @param ntlmDomain the name of the domain to use for + * NTLM authentication. + * + * @since JavaMail 1.4.3 + */ + public synchronized void setNTLMDomain(String ntlmDomain) { + this.ntlmDomain = ntlmDomain; + } + + /** + * Should we report even successful sends by throwing an exception? + * If so, a SendFailedException will always be thrown and + * an {@link com.sun.mail.smtp.SMTPAddressSucceededException + * SMTPAddressSucceededException} will be included in the exception + * chain for each successful address, along with the usual + * {@link com.sun.mail.smtp.SMTPAddressFailedException + * SMTPAddressFailedException} for each unsuccessful address. + * + * @return true if an exception will be thrown on successful sends. + * + * @since JavaMail 1.3.2 + */ + public synchronized boolean getReportSuccess() { + return reportSuccess; + } + + /** + * Set whether successful sends should be reported by throwing + * an exception. + * + * @param reportSuccess should we throw an exception on success? + * + * @since JavaMail 1.3.2 + */ + public synchronized void setReportSuccess(boolean reportSuccess) { + this.reportSuccess = reportSuccess; + } + + /** + * Should we use the STARTTLS command to secure the connection + * if the server supports it? + * + * @return true if the STARTTLS command will be used + * + * @since JavaMail 1.3.2 + */ + public synchronized boolean getStartTLS() { + return useStartTLS; + } + + /** + * Set whether the STARTTLS command should be used. + * + * @param useStartTLS should we use the STARTTLS command? + * + * @since JavaMail 1.3.2 + */ + public synchronized void setStartTLS(boolean useStartTLS) { + this.useStartTLS = useStartTLS; + } + + /** + * Should we require the STARTTLS command to secure the connection? + * + * @return true if the STARTTLS command will be required + * + * @since JavaMail 1.4.2 + */ + public synchronized boolean getRequireStartTLS() { + return requireStartTLS; + } + + /** + * Set whether the STARTTLS command should be required. + * + * @param requireStartTLS should we require the STARTTLS command? + * + * @since JavaMail 1.4.2 + */ + public synchronized void setRequireStartTLS(boolean requireStartTLS) { + this.requireStartTLS = requireStartTLS; + } + + /** + * Is this Transport using SSL to connect to the server? + * + * @return true if using SSL + * @since JavaMail 1.4.6 + */ + public synchronized boolean isSSL() { + return serverSocket instanceof SSLSocket; + } + + /** + * Should we use the RSET command instead of the NOOP command + * in the @{link #isConnected isConnected} method? + * + * @return true if RSET will be used + * + * @since JavaMail 1.4 + */ + public synchronized boolean getUseRset() { + return useRset; + } + + /** + * Set whether the RSET command should be used instead of the + * NOOP command in the @{link #isConnected isConnected} method. + * + * @param useRset should we use the RSET command? + * + * @since JavaMail 1.4 + */ + public synchronized void setUseRset(boolean useRset) { + this.useRset = useRset; + } + + /** + * Is the NOOP command required to return a response code + * of 250 to indicate success? + * + * @return true if NOOP must return 250 + * + * @since JavaMail 1.4.3 + */ + public synchronized boolean getNoopStrict() { + return noopStrict; + } + + /** + * Set whether the NOOP command is required to return a response code + * of 250 to indicate success. + * + * @param noopStrict is NOOP required to return 250? + * + * @since JavaMail 1.4.3 + */ + public synchronized void setNoopStrict(boolean noopStrict) { + this.noopStrict = noopStrict; + } + + /** + * Return the last response we got from the server. + * A failed send is often followed by an RSET command, + * but the response from the RSET command is not saved. + * Instead, this returns the response from the command + * before the RSET command. + * + * @return last response from server + * + * @since JavaMail 1.3.2 + */ + public synchronized String getLastServerResponse() { + return lastServerResponse; + } + + /** + * Return the return code from the last response we got from the server. + * + * @return return code from last response from server + * + * @since JavaMail 1.4.1 + */ + public synchronized int getLastReturnCode() { + return lastReturnCode; + } + + /** + * Performs the actual protocol-specific connection attempt. + * Will attempt to connect to "localhost" if the host was null.

    + * + * Unless mail.smtp.ehlo is set to false, we'll try to identify + * ourselves using the ESMTP command EHLO. + * + * If mail.smtp.auth is set to true, we insist on having a username + * and password, and will try to authenticate ourselves if the server + * supports the AUTH extension (RFC 2554). + * + * @param host the name of the host to connect to + * @param port the port to use (-1 means use default port) + * @param user the name of the user to login as + * @param password the user's password + * @return true if connection successful, false if authentication failed + * @exception MessagingException for non-authentication failures + */ + @Override + protected synchronized boolean protocolConnect(String host, int port, + String user, String password) + throws MessagingException { + Properties props = session.getProperties(); + + // setting mail.smtp.auth to true enables attempts to use AUTH + boolean useAuth = PropUtil.getBooleanProperty(props, + "mail." + name + ".auth", false); + + /* + * If mail.smtp.auth is set, make sure we have a valid username + * and password, even if we might not end up using it (e.g., + * because the server doesn't support ESMTP or doesn't support + * the AUTH extension). + */ + if (useAuth && (user == null || password == null)) { + if (logger.isLoggable(Level.FINE)) { + logger.fine("need username and password for authentication"); + logger.fine("protocolConnect returning false" + + ", host=" + host + + ", user=" + traceUser(user) + + ", password=" + tracePassword(password)); + } + return false; + } + + // setting mail.smtp.ehlo to false disables attempts to use EHLO + boolean useEhlo = PropUtil.getBooleanProperty(props, + "mail." + name + ".ehlo", true); + if (logger.isLoggable(Level.FINE)) + logger.fine("useEhlo " + useEhlo + ", useAuth " + useAuth); + + /* + * If port is not specified, set it to value of mail.smtp.port + * property if it exists, otherwise default to 25. + */ + if (port == -1) + port = PropUtil.getIntProperty(props, + "mail." + name + ".port", -1); + if (port == -1) + port = defaultPort; + + if (host == null || host.length() == 0) + host = "localhost"; + + /* + * If anything goes wrong, we need to be sure + * to close the connection. + */ + boolean connected = false; + try { + + if (serverSocket != null) + openServer(); // only happens from connect(socket) + else + openServer(host, port); + + boolean succeed = false; + if (useEhlo) + succeed = ehlo(getLocalHost()); + if (!succeed) + helo(getLocalHost()); + + if (useStartTLS || requireStartTLS) { + if (serverSocket instanceof SSLSocket) { + logger.fine("STARTTLS requested but already using SSL"); + } else if (supportsExtension("STARTTLS")) { + startTLS(); + /* + * Have to issue another EHLO to update list of extensions + * supported, especially authentication mechanisms. + * Don't know if this could ever fail, but we ignore + * failure. + */ + ehlo(getLocalHost()); + } else if (requireStartTLS) { + logger.fine("STARTTLS required but not supported"); + throw new MessagingException( + "STARTTLS is required but " + + "host does not support STARTTLS"); + } + } + + if (allowutf8 && !supportsExtension("SMTPUTF8")) + logger.log(Level.INFO, "mail.mime.allowutf8 set " + + "but server doesn't advertise SMTPUTF8 support"); + + if ((useAuth || (user != null && password != null)) && + (supportsExtension("AUTH") || + supportsExtension("AUTH=LOGIN"))) { + if (logger.isLoggable(Level.FINE)) + logger.fine("protocolConnect login" + + ", host=" + host + + ", user=" + traceUser(user) + + ", password=" + tracePassword(password)); + connected = authenticate(user, password); + return connected; + } + + // we connected correctly + connected = true; + return true; + + } finally { + // if we didn't connect successfully, + // make sure the connection is closed + if (!connected) { + try { + closeConnection(); + } catch (MessagingException mex) { + // ignore it + } + } + } + } + + /** + * Authenticate to the server. + */ + private boolean authenticate(String user, String passwd) + throws MessagingException { + // setting mail.smtp.auth.mechanisms controls which mechanisms will + // be used, and in what order they'll be considered. only the first + // match is used. + String mechs = session.getProperty("mail." + name + ".auth.mechanisms"); + if (mechs == null) + mechs = defaultAuthenticationMechanisms; + + String authzid = getAuthorizationId(); + if (authzid == null) + authzid = user; + if (enableSASL) { + logger.fine("Authenticate with SASL"); + try { + if (sasllogin(getSASLMechanisms(), getSASLRealm(), authzid, + user, passwd)) { + return true; // success + } else { + logger.fine("SASL authentication failed"); + return false; + } + } catch (UnsupportedOperationException ex) { + logger.log(Level.FINE, "SASL support failed", ex); + // if the SASL support fails, fall back to non-SASL + } + } + + if (logger.isLoggable(Level.FINE)) + logger.fine("Attempt to authenticate using mechanisms: " + mechs); + + /* + * Loop through the list of mechanisms supplied by the user + * (or defaulted) and try each in turn. If the server supports + * the mechanism and we have an authenticator for the mechanism, + * and it hasn't been disabled, use it. + */ + StringTokenizer st = new StringTokenizer(mechs); + while (st.hasMoreTokens()) { + String m = st.nextToken(); + m = m.toUpperCase(Locale.ENGLISH); + Authenticator a = authenticators.get(m); + if (a == null) { + logger.log(Level.FINE, "no authenticator for mechanism {0}", m); + continue; + } + + if (!supportsAuthentication(m)) { + logger.log(Level.FINE, "mechanism {0} not supported by server", + m); + continue; + } + + /* + * If using the default mechanisms, check if this one is disabled. + */ + if (mechs == defaultAuthenticationMechanisms) { + String dprop = "mail." + name + ".auth." + + m.toLowerCase(Locale.ENGLISH) + ".disable"; + boolean disabled = PropUtil.getBooleanProperty( + session.getProperties(), + dprop, !a.enabled()); + if (disabled) { + if (logger.isLoggable(Level.FINE)) + logger.fine("mechanism " + m + + " disabled by property: " + dprop); + continue; + } + } + + // only the first supported and enabled mechanism is used + logger.log(Level.FINE, "Using mechanism {0}", m); + return a.authenticate(host, authzid, user, passwd); + } + + // if no authentication mechanism found, fail + throw new AuthenticationFailedException( + "No authentication mechanisms supported by both server and client"); + } + + /** + * Abstract base class for SMTP authentication mechanism implementations. + */ + private abstract class Authenticator { + protected int resp; // the response code, used by subclasses + private final String mech; // the mechanism name, set in the constructor + private final boolean enabled; // is this mechanism enabled by default? + + Authenticator(String mech) { + this(mech, true); + } + + Authenticator(String mech, boolean enabled) { + this.mech = mech.toUpperCase(Locale.ENGLISH); + this.enabled = enabled; + } + + String getMechanism() { + return mech; + } + + boolean enabled() { + return enabled; + } + + /** + * Start the authentication handshake by issuing the AUTH command. + * Delegate to the doAuth method to do the mechanism-specific + * part of the handshake. + */ + boolean authenticate(String host, String authzid, + String user, String passwd) throws MessagingException { + Throwable thrown = null; + try { + // use "initial response" capability, if supported + String ir = getInitialResponse(host, authzid, user, passwd); + if (noauthdebug && isTracing()) { + logger.fine("AUTH " + mech + " command trace suppressed"); + suspendTracing(); + } + if (ir != null) + resp = simpleCommand("AUTH " + mech + " " + + (ir.length() == 0 ? "=" : ir)); + else + resp = simpleCommand("AUTH " + mech); + + /* + * A 530 response indicates that the server wants us to + * issue a STARTTLS command first. Do that and try again. + */ + if (resp == 530) { + startTLS(); + if (ir != null) + resp = simpleCommand("AUTH " + mech + " " + ir); + else + resp = simpleCommand("AUTH " + mech); + } + if (resp == 334) + doAuth(host, authzid, user, passwd); + } catch (IOException ex) { // should never happen, ignore + logger.log(Level.FINE, "AUTH " + mech + " failed", ex); + } catch (Throwable t) { // crypto can't be initialized? + logger.log(Level.FINE, "AUTH " + mech + " failed", t); + thrown = t; + } finally { + if (noauthdebug && isTracing()) + logger.fine("AUTH " + mech + " " + + (resp == 235 ? "succeeded" : "failed")); + resumeTracing(); + if (resp != 235) { + closeConnection(); + if (thrown != null) { + if (thrown instanceof Error) + throw (Error)thrown; + if (thrown instanceof Exception) + throw new AuthenticationFailedException( + getLastServerResponse(), + (Exception)thrown); + assert false : "unknown Throwable"; // can't happen + } + throw new AuthenticationFailedException( + getLastServerResponse()); + } + } + return true; + } + + /** + * Provide the initial response to use in the AUTH command, + * or null if not supported. Subclasses that support the + * initial response capability will override this method. + */ + String getInitialResponse(String host, String authzid, String user, + String passwd) throws MessagingException, IOException { + return null; + } + + abstract void doAuth(String host, String authzid, String user, + String passwd) throws MessagingException, IOException; + } + + /** + * Perform the authentication handshake for LOGIN authentication. + */ + private class LoginAuthenticator extends Authenticator { + LoginAuthenticator() { + super("LOGIN"); + } + + @Override + void doAuth(String host, String authzid, String user, String passwd) + throws MessagingException, IOException { + // send username + resp = simpleCommand(BASE64EncoderStream.encode( + user.getBytes(StandardCharsets.UTF_8))); + if (resp == 334) { + // send passwd + resp = simpleCommand(BASE64EncoderStream.encode( + passwd.getBytes(StandardCharsets.UTF_8))); + } + } + } + + /** + * Perform the authentication handshake for PLAIN authentication. + */ + private class PlainAuthenticator extends Authenticator { + PlainAuthenticator() { + super("PLAIN"); + } + + @Override + String getInitialResponse(String host, String authzid, String user, + String passwd) throws MessagingException, IOException { + // return "authziduserpasswd" + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + OutputStream b64os = + new BASE64EncoderStream(bos, Integer.MAX_VALUE); + if (authzid != null) + b64os.write(authzid.getBytes(StandardCharsets.UTF_8)); + b64os.write(0); + b64os.write(user.getBytes(StandardCharsets.UTF_8)); + b64os.write(0); + b64os.write(passwd.getBytes(StandardCharsets.UTF_8)); + b64os.flush(); // complete the encoding + + return ASCIIUtility.toString(bos.toByteArray()); + } + + @Override + void doAuth(String host, String authzid, String user, String passwd) + throws MessagingException, IOException { + // should never get here + throw new AuthenticationFailedException("PLAIN asked for more"); + } + } + + /** + * Perform the authentication handshake for DIGEST-MD5 authentication. + */ + private class DigestMD5Authenticator extends Authenticator { + private DigestMD5 md5support; // only create if needed + + DigestMD5Authenticator() { + super("DIGEST-MD5"); + } + + private synchronized DigestMD5 getMD5() { + if (md5support == null) + md5support = new DigestMD5(logger); + return md5support; + } + + @Override + void doAuth(String host, String authzid, String user, String passwd) + throws MessagingException, IOException { + DigestMD5 md5 = getMD5(); + assert md5 != null; + + byte[] b = md5.authClient(host, user, passwd, getSASLRealm(), + getLastServerResponse()); + resp = simpleCommand(b); + if (resp == 334) { // client authenticated by server + if (!md5.authServer(getLastServerResponse())) { + // server NOT authenticated by client !!! + resp = -1; + } else { + // send null response + resp = simpleCommand(new byte[0]); + } + } + } + } + + /** + * Perform the authentication handshake for NTLM authentication. + */ + private class NtlmAuthenticator extends Authenticator { + private Ntlm ntlm; + private int flags; + + NtlmAuthenticator() { + super("NTLM"); + } + + @Override + String getInitialResponse(String host, String authzid, String user, + String passwd) throws MessagingException, IOException { + ntlm = new Ntlm(getNTLMDomain(), getLocalHost(), + user, passwd, logger); + + flags = PropUtil.getIntProperty( + session.getProperties(), + "mail." + name + ".auth.ntlm.flags", 0); + + String type1 = ntlm.generateType1Msg(flags); + return type1; + } + + @Override + void doAuth(String host, String authzid, String user, String passwd) + throws MessagingException, IOException { + assert ntlm != null; + String type3 = ntlm.generateType3Msg( + getLastServerResponse().substring(4).trim()); + + resp = simpleCommand(type3); + } + } + + /** + * Perform the authentication handshake for XOAUTH2 authentication. + */ + private class OAuth2Authenticator extends Authenticator { + + OAuth2Authenticator() { + super("XOAUTH2", false); // disabled by default + } + + @Override + String getInitialResponse(String host, String authzid, String user, + String passwd) throws MessagingException, IOException { + String resp = "user=" + user + "\001auth=Bearer " + + passwd + "\001\001"; + byte[] b = BASE64EncoderStream.encode( + resp.getBytes(StandardCharsets.UTF_8)); + return ASCIIUtility.toString(b); + } + + @Override + void doAuth(String host, String authzid, String user, String passwd) + throws MessagingException, IOException { + // should never get here + throw new AuthenticationFailedException("OAUTH2 asked for more"); + } + } + + /** + * SASL-based login. + * + * @param allowed the allowed SASL mechanisms + * @param realm the SASL realm + * @param authzid the authorization ID + * @param u the user name for authentication + * @param p the password for authentication + * @return true for success + * @exception MessagingException for failures + */ + private boolean sasllogin(String[] allowed, String realm, String authzid, + String u, String p) throws MessagingException { + String serviceHost; + if (useCanonicalHostName) + serviceHost = serverSocket.getInetAddress().getCanonicalHostName(); + else + serviceHost = host; + if (saslAuthenticator == null) { + try { + Class sac = Class.forName( + "com.sun.mail.smtp.SMTPSaslAuthenticator"); + Constructor c = sac.getConstructor(new Class[] { + SMTPTransport.class, + String.class, + Properties.class, + MailLogger.class, + String.class + }); + saslAuthenticator = (SaslAuthenticator)c.newInstance( + new Object[] { + this, + name, + session.getProperties(), + logger, + serviceHost + }); + } catch (Exception ex) { + logger.log(Level.FINE, "Can't load SASL authenticator", ex); + // probably because we're running on a system without SASL + return false; // not authenticated, try without SASL + } + } + + // were any allowed mechanisms specified? + List v; + if (allowed != null && allowed.length > 0) { + // remove anything not supported by the server + v = new ArrayList<>(allowed.length); + for (int i = 0; i < allowed.length; i++) + if (supportsAuthentication(allowed[i])) // XXX - case must match + v.add(allowed[i]); + } else { + // everything is allowed + v = new ArrayList<>(); + if (extMap != null) { + String a = extMap.get("AUTH"); + if (a != null) { + StringTokenizer st = new StringTokenizer(a); + while (st.hasMoreTokens()) + v.add(st.nextToken()); + } + } + } + String[] mechs = v.toArray(new String[v.size()]); + try { + if (noauthdebug && isTracing()) { + logger.fine("SASL AUTH command trace suppressed"); + suspendTracing(); + } + return saslAuthenticator.authenticate(mechs, realm, authzid, u, p); + } finally { + resumeTracing(); + } + } + + /** + * Send the Message to the specified list of addresses.

    + * + * If all the addresses succeed the SMTP check + * using the RCPT TO: command, we attempt to send the message. + * A TransportEvent of type MESSAGE_DELIVERED is fired indicating the + * successful submission of a message to the SMTP host.

    + * + * If some of the addresses fail the SMTP check, + * and the mail.smtp.sendpartial property is not set, + * sending is aborted. The TransportEvent of type MESSAGE_NOT_DELIVERED + * is fired containing the valid and invalid addresses. The + * SendFailedException is also thrown.

    + * + * If some of the addresses fail the SMTP check, + * and the mail.smtp.sendpartial property is set to true, + * the message is sent. The TransportEvent of type + * MESSAGE_PARTIALLY_DELIVERED + * is fired containing the valid and invalid addresses. The + * SMTPSendFailedException is also thrown.

    + * + * MessagingException is thrown if the message can't write out + * an RFC822-compliant stream using its writeTo method.

    + * + * @param message The MimeMessage to be sent + * @param addresses List of addresses to send this message to + * @see javax.mail.event.TransportEvent + * @exception SMTPSendFailedException if the send failed because of + * an SMTP command error + * @exception SendFailedException if the send failed because of + * invalid addresses. + * @exception MessagingException if the connection is dead + * or not in the connected state or if the message is + * not a MimeMessage. + */ + @Override + public synchronized void sendMessage(Message message, Address[] addresses) + throws MessagingException, SendFailedException { + + sendMessageStart(message != null ? message.getSubject() : ""); + checkConnected(); + + // check if the message is a valid MIME/RFC822 message and that + // it has all valid InternetAddresses; fail if not + if (!(message instanceof MimeMessage)) { + logger.fine("Can only send RFC822 msgs"); + throw new MessagingException("SMTP can only send RFC822 messages"); + } + for (int i = 0; i < addresses.length; i++) { + if (!(addresses[i] instanceof InternetAddress)) { + throw new MessagingException(addresses[i] + + " is not an InternetAddress"); + } + } + if (addresses.length == 0) + throw new SendFailedException("No recipient addresses"); + + this.message = (MimeMessage)message; + this.addresses = addresses; + validUnsentAddr = addresses; // until we know better + expandGroups(); + + boolean use8bit = false; + if (message instanceof SMTPMessage) + use8bit = ((SMTPMessage)message).getAllow8bitMIME(); + if (!use8bit) + use8bit = PropUtil.getBooleanProperty(session.getProperties(), + "mail." + name + ".allow8bitmime", false); + if (logger.isLoggable(Level.FINE)) + logger.fine("use8bit " + use8bit); + if (use8bit && supportsExtension("8BITMIME")) { + if (convertTo8Bit(this.message)) { + // in case we made any changes, save those changes + // XXX - this will change the Message-ID + try { + this.message.saveChanges(); + } catch (MessagingException mex) { + // ignore it + } + } + } + + try { + mailFrom(); + rcptTo(); + if (chunkSize > 0 && supportsExtension("CHUNKING")) { + /* + * Use BDAT to send the data in chunks. + * Note that even though the BDAT command is able to send + * messages that contain binary data, we can't use it to + * do that because a) we still need to canonicalize the + * line terminators for text data, which we can't tell apart + * from the message content, and b) the message content is + * encoded before we even know that we can use BDAT. + */ + this.message.writeTo(bdat(), ignoreList); + finishBdat(); + } else { + this.message.writeTo(data(), ignoreList); + finishData(); + } + if (sendPartiallyFailed) { + // throw the exception, + // fire TransportEvent.MESSAGE_PARTIALLY_DELIVERED event + logger.fine("Sending partially failed " + + "because of invalid destination addresses"); + notifyTransportListeners( + TransportEvent.MESSAGE_PARTIALLY_DELIVERED, + validSentAddr, validUnsentAddr, invalidAddr, + this.message); + + throw new SMTPSendFailedException(".", lastReturnCode, + lastServerResponse, exception, + validSentAddr, validUnsentAddr, invalidAddr); + } + logger.fine("message successfully delivered to mail server"); + notifyTransportListeners(TransportEvent.MESSAGE_DELIVERED, + validSentAddr, validUnsentAddr, + invalidAddr, this.message); + } catch (MessagingException mex) { + logger.log(Level.FINE, "MessagingException while sending", mex); + // the MessagingException might be wrapping an IOException + if (mex.getNextException() instanceof IOException) { + // if we catch an IOException, it means that we want + // to drop the connection so that the message isn't sent + logger.fine("nested IOException, closing"); + try { + closeConnection(); + } catch (MessagingException cex) { /* ignore it */ } + } + addressesFailed(); + notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, + validSentAddr, validUnsentAddr, + invalidAddr, this.message); + + throw mex; + } catch (IOException ex) { + logger.log(Level.FINE, "IOException while sending, closing", ex); + // if we catch an IOException, it means that we want + // to drop the connection so that the message isn't sent + try { + closeConnection(); + } catch (MessagingException mex) { /* ignore it */ } + addressesFailed(); + notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, + validSentAddr, validUnsentAddr, + invalidAddr, this.message); + + throw new MessagingException("IOException while sending message", + ex); + } finally { + // no reason to keep this data around + validSentAddr = validUnsentAddr = invalidAddr = null; + this.addresses = null; + this.message = null; + this.exception = null; + sendPartiallyFailed = false; + notificationDone = false; // reset for next send + } + sendMessageEnd(); + } + + /** + * The send failed, fix the address arrays to report the failure correctly. + */ + private void addressesFailed() { + if (validSentAddr != null) { + if (validUnsentAddr != null) { + Address newa[] = + new Address[validSentAddr.length + validUnsentAddr.length]; + System.arraycopy(validSentAddr, 0, + newa, 0, validSentAddr.length); + System.arraycopy(validUnsentAddr, 0, + newa, validSentAddr.length, validUnsentAddr.length); + validSentAddr = null; + validUnsentAddr = newa; + } else { + validUnsentAddr = validSentAddr; + validSentAddr = null; + } + } + } + + /** + * Close the Transport and terminate the connection to the server. + */ + @Override + public synchronized void close() throws MessagingException { + if (!super.isConnected()) // Already closed. + return; + try { + if (serverSocket != null) { + sendCommand("QUIT"); + if (quitWait) { + int resp = readServerResponse(); + if (resp != 221 && resp != -1 && + logger.isLoggable(Level.FINE)) + logger.fine("QUIT failed with " + resp); + } + } + } finally { + closeConnection(); + } + } + + private void closeConnection() throws MessagingException { + try { + if (serverSocket != null) + serverSocket.close(); + } catch (IOException ioex) { // shouldn't happen + throw new MessagingException("Server Close Failed", ioex); + } finally { + serverSocket = null; + serverOutput = null; + serverInput = null; + lineInputStream = null; + if (super.isConnected()) // only notify if already connected + super.close(); + } + } + + /** + * Check whether the transport is connected. Override superclass + * method, to actually ping our server connection. + */ + @Override + public synchronized boolean isConnected() { + if (!super.isConnected()) + // if we haven't been connected at all, don't bother with NOOP + return false; + + try { + // sendmail may respond slowly to NOOP after many requests + // so if mail.smtp.userset is set we use RSET instead of NOOP. + if (useRset) + sendCommand("RSET"); + else + sendCommand("NOOP"); + int resp = readServerResponse(); + + /* + * NOOP should return 250 on success, however, SIMS 3.2 returns + * 200, so we work around it. + * + * Hotmail didn't used to implement the NOOP command at all so + * assume any kind of response means we're still connected. + * That is, any response except 421, which means the server + * is shutting down the connection. + * + * Some versions of Exchange return 451 instead of 421 when + * timing out a connection. + * + * Argh! + * + * If mail.smtp.noop.strict is set to false, be tolerant of + * servers that return the wrong response code for success. + */ + if (resp >= 0 && (noopStrict ? resp == 250 : resp != 421)) { + return true; + } else { + try { + closeConnection(); + } catch (MessagingException mex) { + // ignore it + } + return false; + } + } catch (Exception ex) { + try { + closeConnection(); + } catch (MessagingException mex) { + // ignore it + } + return false; + } + } + + /** + * Notify all TransportListeners. Keep track of whether notification + * has been done so as to only notify once per send. + * + * @since JavaMail 1.4.2 + */ + @Override + protected void notifyTransportListeners(int type, Address[] validSent, + Address[] validUnsent, + Address[] invalid, Message msg) { + + if (!notificationDone) { + super.notifyTransportListeners(type, validSent, validUnsent, + invalid, msg); + notificationDone = true; + } + } + + /** + * Expand any group addresses. + */ + private void expandGroups() { + List

    groups = null; + for (int i = 0; i < addresses.length; i++) { + InternetAddress a = (InternetAddress)addresses[i]; + if (a.isGroup()) { + if (groups == null) { + // first group, catch up with where we are + groups = new ArrayList<>(); + for (int k = 0; k < i; k++) + groups.add(addresses[k]); + } + // parse it and add each individual address + try { + InternetAddress[] ia = a.getGroup(true); + if (ia != null) { + for (int j = 0; j < ia.length; j++) + groups.add(ia[j]); + } else + groups.add(a); + } catch (ParseException pex) { + // parse failed, add the whole thing + groups.add(a); + } + } else { + // if we've started accumulating a list, add this to it + if (groups != null) + groups.add(a); + } + } + + // if we have a new list, convert it back to an array + if (groups != null) { + InternetAddress[] newa = new InternetAddress[groups.size()]; + groups.toArray(newa); + addresses = newa; + } + } + + /** + * If the Part is a text part and has a Content-Transfer-Encoding + * of "quoted-printable" or "base64", and it obeys the rules for + * "8bit" encoding, change the encoding to "8bit". If the part is + * a multipart, recursively process all its parts. + * + * @return true if any changes were made + * + * XXX - This is really quite a hack. + */ + private boolean convertTo8Bit(MimePart part) { + boolean changed = false; + try { + if (part.isMimeType("text/*")) { + String enc = part.getEncoding(); + if (enc != null && (enc.equalsIgnoreCase("quoted-printable") || + enc.equalsIgnoreCase("base64"))) { + InputStream is = null; + try { + is = part.getInputStream(); + if (is8Bit(is)) { + /* + * If the message was created using an InputStream + * then we have to extract the content as an object + * and set it back as an object so that the content + * will be re-encoded. + * + * If the message was not created using an + * InputStream, the following should have no effect. + */ + part.setContent(part.getContent(), + part.getContentType()); + part.setHeader("Content-Transfer-Encoding", "8bit"); + changed = true; + } + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException ex2) { + // ignore it + } + } + } + } + } else if (part.isMimeType("multipart/*")) { + MimeMultipart mp = (MimeMultipart)part.getContent(); + int count = mp.getCount(); + for (int i = 0; i < count; i++) { + if (convertTo8Bit((MimePart)mp.getBodyPart(i))) + changed = true; + } + } + } catch (IOException ioex) { + // any exception causes us to give up + } catch (MessagingException mex) { + // any exception causes us to give up + } + return changed; + } + + /** + * Check whether the data in the given InputStream follows the + * rules for 8bit text. Lines have to be 998 characters or less + * and no NULs are allowed. CR and LF must occur in pairs but we + * don't check that because we assume this is text and we convert + * all CR/LF combinations into canonical CRLF later. + */ + private boolean is8Bit(InputStream is) { + int b; + int linelen = 0; + boolean need8bit = false; + try { + while ((b = is.read()) >= 0) { + b &= 0xff; + if (b == '\r' || b == '\n') + linelen = 0; + else if (b == 0) + return false; + else { + linelen++; + if (linelen > 998) // 1000 - CRLF + return false; + } + if (b > 0x7f) + need8bit = true; + } + } catch (IOException ex) { + return false; + } + if (need8bit) + logger.fine("found an 8bit part"); + return need8bit; + } + + @Override + protected void finalize() throws Throwable { + try { + closeConnection(); + } catch (MessagingException mex) { + // ignore it + } finally { + super.finalize(); + } + } + + ///////////////////// smtp stuff /////////////////////// + private BufferedInputStream serverInput; + private LineInputStream lineInputStream; + private OutputStream serverOutput; + private Socket serverSocket; + private TraceInputStream traceInput; + private TraceOutputStream traceOutput; + + /////// smtp protocol ////// + + /** + * Issue the HELO command. + * + * @param domain our domain + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + protected void helo(String domain) throws MessagingException { + if (domain != null) + issueCommand("HELO " + domain, 250); + else + issueCommand("HELO", 250); + } + + /** + * Issue the EHLO command. + * Collect the returned list of service extensions. + * + * @param domain our domain + * @return true if command succeeds + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + protected boolean ehlo(String domain) throws MessagingException { + String cmd; + if (domain != null) + cmd = "EHLO " + domain; + else + cmd = "EHLO"; + sendCommand(cmd); + int resp = readServerResponse(); + if (resp == 250) { + // extract the supported service extensions + BufferedReader rd = + new BufferedReader(new StringReader(lastServerResponse)); + String line; + extMap = new Hashtable<>(); + try { + boolean first = true; + while ((line = rd.readLine()) != null) { + if (first) { // skip first line which is the greeting + first = false; + continue; + } + if (line.length() < 5) + continue; // shouldn't happen + line = line.substring(4); // skip response code + int i = line.indexOf(' '); + String arg = ""; + if (i > 0) { + arg = line.substring(i + 1); + line = line.substring(0, i); + } + if (logger.isLoggable(Level.FINE)) + logger.fine("Found extension \"" + + line + "\", arg \"" + arg + "\""); + extMap.put(line.toUpperCase(Locale.ENGLISH), arg); + } + } catch (IOException ex) { } // can't happen + } + return resp == 250; + } + + /** + * Issue the MAIL FROM: command to start sending a message.

    + * + * Gets the sender's address in the following order: + *

      + *
    1. SMTPMessage.getEnvelopeFrom()
    2. + *
    3. mail.smtp.from property
    4. + *
    5. From: header in the message
    6. + *
    7. System username using the + * InternetAddress.getLocalAddress() method
    8. + *
    + * + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + protected void mailFrom() throws MessagingException { + String from = null; + if (message instanceof SMTPMessage) + from = ((SMTPMessage)message).getEnvelopeFrom(); + if (from == null || from.length() <= 0) + from = session.getProperty("mail." + name + ".from"); + if (from == null || from.length() <= 0) { + Address[] fa; + Address me; + if (message != null && (fa = message.getFrom()) != null && + fa.length > 0) + me = fa[0]; + else + me = InternetAddress.getLocalAddress(session); + + if (me != null) + from = ((InternetAddress)me).getAddress(); + else + throw new MessagingException( + "can't determine local email address"); + } + + String cmd = "MAIL FROM:" + normalizeAddress(from); + + if (allowutf8 && supportsExtension("SMTPUTF8")) + cmd += " SMTPUTF8"; + + // request delivery status notification? + if (supportsExtension("DSN")) { + String ret = null; + if (message instanceof SMTPMessage) + ret = ((SMTPMessage)message).getDSNRet(); + if (ret == null) + ret = session.getProperty("mail." + name + ".dsn.ret"); + // XXX - check for legal syntax? + if (ret != null) + cmd += " RET=" + ret; + } + + /* + * If an RFC 2554 submitter has been specified, and the server + * supports the AUTH extension, include the AUTH= element on + * the MAIL FROM command. + */ + if (supportsExtension("AUTH")) { + String submitter = null; + if (message instanceof SMTPMessage) + submitter = ((SMTPMessage)message).getSubmitter(); + if (submitter == null) + submitter = session.getProperty("mail." + name + ".submitter"); + // XXX - check for legal syntax? + if (submitter != null) { + try { + String s = xtext(submitter, + allowutf8 && supportsExtension("SMTPUTF8")); + cmd += " AUTH=" + s; + } catch (IllegalArgumentException ex) { + if (logger.isLoggable(Level.FINE)) + logger.log(Level.FINE, "ignoring invalid submitter: " + + submitter, ex); + } + } + } + + /* + * Have any extensions to the MAIL command been specified? + */ + String ext = null; + if (message instanceof SMTPMessage) + ext = ((SMTPMessage)message).getMailExtension(); + if (ext == null) + ext = session.getProperty("mail." + name + ".mailextension"); + if (ext != null && ext.length() > 0) + cmd += " " + ext; + + try { + issueSendCommand(cmd, 250); + } catch (SMTPSendFailedException ex) { + int retCode = ex.getReturnCode(); + switch (retCode) { + case 550: case 553: case 503: case 551: case 501: + // given address is invalid + try { + ex.setNextException(new SMTPSenderFailedException( + new InternetAddress(from), cmd, + retCode, ex.getMessage())); + } catch (AddressException aex) { + // oh well... + } + break; + default: + break; + } + throw ex; + } + } + + /** + * Sends each address to the SMTP host using the RCPT TO: + * command and copies the address either into + * the validSentAddr or invalidAddr arrays. + * Sets the sendFailed + * flag to true if any addresses failed. + * + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + /* + * success/failure/error possibilities from the RCPT command + * from rfc821, section 4.3 + * S: 250, 251 + * F: 550, 551, 552, 553, 450, 451, 452 + * E: 500, 501, 503, 421 + * + * and how we map the above error/failure conditions to valid/invalid + * address lists that are reported in the thrown exception: + * invalid addr: 550, 501, 503, 551, 553 + * valid addr: 552 (quota), 450, 451, 452 (quota), 421 (srvr abort) + */ + protected void rcptTo() throws MessagingException { + List valid = new ArrayList<>(); + List validUnsent = new ArrayList<>(); + List invalid = new ArrayList<>(); + int retCode = -1; + MessagingException mex = null; + boolean sendFailed = false; + MessagingException sfex = null; + validSentAddr = validUnsentAddr = invalidAddr = null; + boolean sendPartial = false; + if (message instanceof SMTPMessage) + sendPartial = ((SMTPMessage)message).getSendPartial(); + if (!sendPartial) + sendPartial = PropUtil.getBooleanProperty(session.getProperties(), + "mail." + name + ".sendpartial", false); + if (sendPartial) + logger.fine("sendPartial set"); + + boolean dsn = false; + String notify = null; + if (supportsExtension("DSN")) { + if (message instanceof SMTPMessage) + notify = ((SMTPMessage)message).getDSNNotify(); + if (notify == null) + notify = session.getProperty("mail." + name + ".dsn.notify"); + // XXX - check for legal syntax? + if (notify != null) + dsn = true; + } + + // try the addresses one at a time + for (int i = 0; i < addresses.length; i++) { + + sfex = null; + InternetAddress ia = (InternetAddress)addresses[i]; + String cmd = "RCPT TO:" + normalizeAddress(ia.getAddress()); + if (dsn) + cmd += " NOTIFY=" + notify; + // send the addresses to the SMTP server + sendCommand(cmd); + // check the server's response for address validity + retCode = readServerResponse(); + switch (retCode) { + case 250: case 251: + valid.add(ia); + if (!reportSuccess) + break; + + // user wants exception even when successful, including + // details of the return code + + // create and chain the exception + sfex = new SMTPAddressSucceededException(ia, cmd, retCode, + lastServerResponse); + if (mex == null) + mex = sfex; + else + mex.setNextException(sfex); + break; + + case 550: case 553: case 503: case 551: case 501: + // given address is invalid + if (!sendPartial) + sendFailed = true; + invalid.add(ia); + // create and chain the exception + sfex = new SMTPAddressFailedException(ia, cmd, retCode, + lastServerResponse); + if (mex == null) + mex = sfex; + else + mex.setNextException(sfex); + break; + + case 552: case 450: case 451: case 452: + // given address is valid + if (!sendPartial) + sendFailed = true; + validUnsent.add(ia); + // create and chain the exception + sfex = new SMTPAddressFailedException(ia, cmd, retCode, + lastServerResponse); + if (mex == null) + mex = sfex; + else + mex.setNextException(sfex); + break; + + default: + // handle remaining 4xy & 5xy codes + if (retCode >= 400 && retCode <= 499) { + // assume address is valid, although we don't really know + validUnsent.add(ia); + } else if (retCode >= 500 && retCode <= 599) { + // assume address is invalid, although we don't really know + invalid.add(ia); + } else { + // completely unexpected response, just give up + if (logger.isLoggable(Level.FINE)) + logger.fine("got response code " + retCode + + ", with response: " + lastServerResponse); + String _lsr = lastServerResponse; // else rset will nuke it + int _lrc = lastReturnCode; + if (serverSocket != null) // hasn't already been closed + issueCommand("RSET", -1); + lastServerResponse = _lsr; // restore, for get + lastReturnCode = _lrc; + throw new SMTPAddressFailedException(ia, cmd, retCode, + _lsr); + } + if (!sendPartial) + sendFailed = true; + // create and chain the exception + sfex = new SMTPAddressFailedException(ia, cmd, retCode, + lastServerResponse); + if (mex == null) + mex = sfex; + else + mex.setNextException(sfex); + break; + } + } + + // if we're willing to send to a partial list, and we found no + // valid addresses, that's complete failure + if (sendPartial && valid.size() == 0) + sendFailed = true; + + // copy the lists into appropriate arrays + if (sendFailed) { + // copy invalid addrs + invalidAddr = new Address[invalid.size()]; + invalid.toArray(invalidAddr); + + // copy all valid addresses to validUnsent, since something failed + validUnsentAddr = new Address[valid.size() + validUnsent.size()]; + int i = 0; + for (int j = 0; j < valid.size(); j++) + validUnsentAddr[i++] = (Address)valid.get(j); + for (int j = 0; j < validUnsent.size(); j++) + validUnsentAddr[i++] = (Address)validUnsent.get(j); + } else if (reportSuccess || (sendPartial && + (invalid.size() > 0 || validUnsent.size() > 0))) { + // we'll go on to send the message, but after sending we'll + // throw an exception with this exception nested + sendPartiallyFailed = true; + exception = mex; + + // copy invalid addrs + invalidAddr = new Address[invalid.size()]; + invalid.toArray(invalidAddr); + + // copy valid unsent addresses to validUnsent + validUnsentAddr = new Address[validUnsent.size()]; + validUnsent.toArray(validUnsentAddr); + + // copy valid addresses to validSent + validSentAddr = new Address[valid.size()]; + valid.toArray(validSentAddr); + } else { // all addresses pass + validSentAddr = addresses; + } + + + // print out the debug info + if (logger.isLoggable(Level.FINE)) { + if (validSentAddr != null && validSentAddr.length > 0) { + logger.fine("Verified Addresses"); + for (int l = 0; l < validSentAddr.length; l++) { + logger.fine(" " + validSentAddr[l]); + } + } + if (validUnsentAddr != null && validUnsentAddr.length > 0) { + logger.fine("Valid Unsent Addresses"); + for (int j = 0; j < validUnsentAddr.length; j++) { + logger.fine(" " + validUnsentAddr[j]); + } + } + if (invalidAddr != null && invalidAddr.length > 0) { + logger.fine("Invalid Addresses"); + for (int k = 0; k < invalidAddr.length; k++) { + logger.fine(" " + invalidAddr[k]); + } + } + } + + // throw the exception, fire TransportEvent.MESSAGE_NOT_DELIVERED event + if (sendFailed) { + logger.fine( + "Sending failed because of invalid destination addresses"); + notifyTransportListeners(TransportEvent.MESSAGE_NOT_DELIVERED, + validSentAddr, validUnsentAddr, + invalidAddr, this.message); + + // reset the connection so more sends are allowed + String lsr = lastServerResponse; // save, for get + int lrc = lastReturnCode; + try { + if (serverSocket != null) + issueCommand("RSET", -1); + } catch (MessagingException ex) { + // if can't reset, best to close the connection + try { + close(); + } catch (MessagingException ex2) { + // thrown by close()--ignore, will close() later anyway + logger.log(Level.FINE, "close failed", ex2); + } + } finally { + lastServerResponse = lsr; // restore + lastReturnCode = lrc; + } + + throw new SendFailedException("Invalid Addresses", mex, + validSentAddr, + validUnsentAddr, invalidAddr); + } + } + + /** + * Send the DATA command to the SMTP host and return + * an OutputStream to which the data is to be written. + * + * @return the stream to write to + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + protected OutputStream data() throws MessagingException { + assert Thread.holdsLock(this); + issueSendCommand("DATA", 354); + dataStream = new SMTPOutputStream(serverOutput); + return dataStream; + } + + /** + * Terminate the sent data. + * + * @exception IOException for I/O errors + * @exception MessagingException for other failures + * @since JavaMail 1.4.1 + */ + protected void finishData() throws IOException, MessagingException { + assert Thread.holdsLock(this); + dataStream.ensureAtBOL(); + issueSendCommand(".", 250); + } + + /** + * Return a stream that will use the SMTP BDAT command to send data. + * + * @return the stream to write to + * @exception MessagingException for failures + * @since JavaMail 1.6.0 + */ + protected OutputStream bdat() throws MessagingException { + assert Thread.holdsLock(this); + dataStream = new BDATOutputStream(serverOutput, chunkSize); + return dataStream; + } + + /** + * Terminate the sent data. + * + * @exception IOException for I/O errors + * @exception MessagingException for other failures + * @since JavaMail 1.6.0 + */ + protected void finishBdat() throws IOException, MessagingException { + assert Thread.holdsLock(this); + dataStream.ensureAtBOL(); + dataStream.close(); // doesn't close underlying socket + } + + /** + * Issue the STARTTLS command and switch the socket to + * TLS mode if it succeeds. + * + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + protected void startTLS() throws MessagingException { + issueCommand("STARTTLS", 220); + // it worked, now switch the socket into TLS mode + try { + serverSocket = SocketFetcher.startTLS(serverSocket, host, + session.getProperties(), "mail." + name); + initStreams(); + } catch (IOException ioex) { + closeConnection(); + throw new MessagingException("Could not convert socket to TLS", + ioex); + } + } + + /////// primitives /////// + + /** + * Connect to host on port and start the SMTP protocol. + */ + private void openServer(String host, int port) + throws MessagingException { + + if (logger.isLoggable(Level.FINE)) + logger.fine("trying to connect to host \"" + host + + "\", port " + port + ", isSSL " + isSSL); + + try { + Properties props = session.getProperties(); + + serverSocket = SocketFetcher.getSocket(host, port, + props, "mail." + name, isSSL); + + // socket factory may've chosen a different port, + // update it for the debug messages that follow + port = serverSocket.getPort(); + // save host name for startTLS + this.host = host; + + initStreams(); + + int r = -1; + if ((r = readServerResponse()) != 220) { + serverSocket.close(); + serverSocket = null; + serverOutput = null; + serverInput = null; + lineInputStream = null; + if (logger.isLoggable(Level.FINE)) + logger.fine("could not connect to host \"" + + host + "\", port: " + port + + ", response: " + r); + throw new MessagingException( + "Could not connect to SMTP host: " + host + + ", port: " + port + + ", response: " + r); + } else { + if (logger.isLoggable(Level.FINE)) + logger.fine("connected to host \"" + + host + "\", port: " + port); + } + } catch (UnknownHostException uhex) { + throw new MessagingException("Unknown SMTP host: " + host, uhex); + } catch (SocketConnectException scex) { + throw new MailConnectException(scex); + } catch (IOException ioe) { + throw new MessagingException("Could not connect to SMTP host: " + + host + ", port: " + port, ioe); + } + } + + /** + * Start the protocol to the server on serverSocket, + * assumed to be provided and connected by the caller. + */ + private void openServer() throws MessagingException { + int port = -1; + host = "UNKNOWN"; + try { + port = serverSocket.getPort(); + host = serverSocket.getInetAddress().getHostName(); + if (logger.isLoggable(Level.FINE)) + logger.fine("starting protocol to host \"" + + host + "\", port " + port); + + initStreams(); + + int r = -1; + if ((r = readServerResponse()) != 220) { + serverSocket.close(); + serverSocket = null; + serverOutput = null; + serverInput = null; + lineInputStream = null; + if (logger.isLoggable(Level.FINE)) + logger.fine("got bad greeting from host \"" + + host + "\", port: " + port + + ", response: " + r); + throw new MessagingException( + "Got bad greeting from SMTP host: " + host + + ", port: " + port + + ", response: " + r); + } else { + if (logger.isLoggable(Level.FINE)) + logger.fine("protocol started to host \"" + + host + "\", port: " + port); + } + } catch (IOException ioe) { + throw new MessagingException( + "Could not start protocol to SMTP host: " + + host + ", port: " + port, ioe); + } + } + + + private void initStreams() throws IOException { + boolean quote = PropUtil.getBooleanProperty(session.getProperties(), + "mail.debug.quote", false); + + traceInput = + new TraceInputStream(serverSocket.getInputStream(), traceLogger); + traceInput.setQuote(quote); + + traceOutput = + new TraceOutputStream(serverSocket.getOutputStream(), traceLogger); + traceOutput.setQuote(quote); + + serverOutput = + new BufferedOutputStream(traceOutput); + serverInput = + new BufferedInputStream(traceInput); + lineInputStream = new LineInputStream(serverInput); + } + + /** + * Is protocol tracing enabled? + */ + private boolean isTracing() { + return traceLogger.isLoggable(Level.FINEST); + } + + /** + * Temporarily turn off protocol tracing, e.g., to prevent + * tracing the authentication sequence, including the password. + */ + private void suspendTracing() { + if (traceLogger.isLoggable(Level.FINEST)) { + traceInput.setTrace(false); + traceOutput.setTrace(false); + } + } + + /** + * Resume protocol tracing, if it was enabled to begin with. + */ + private void resumeTracing() { + if (traceLogger.isLoggable(Level.FINEST)) { + traceInput.setTrace(true); + traceOutput.setTrace(true); + } + } + + /** + * Send the command to the server. If the expected response code + * is not received, throw a MessagingException. + * + * @param cmd the command to send + * @param expect the expected response code (-1 means don't care) + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + public synchronized void issueCommand(String cmd, int expect) + throws MessagingException { + sendCommand(cmd); + + // if server responded with an unexpected return code, + // throw the exception, notifying the client of the response + int resp = readServerResponse(); + if (expect != -1 && resp != expect) + throw new MessagingException(lastServerResponse); + } + + /** + * Issue a command that's part of sending a message. + */ + private void issueSendCommand(String cmd, int expect) + throws MessagingException { + sendCommand(cmd); + + // if server responded with an unexpected return code, + // throw the exception, notifying the client of the response + int ret; + if ((ret = readServerResponse()) != expect) { + // assume message was not sent to anyone, + // combine valid sent & unsent addresses + int vsl = validSentAddr == null ? 0 : validSentAddr.length; + int vul = validUnsentAddr == null ? 0 : validUnsentAddr.length; + Address[] valid = new Address[vsl + vul]; + if (vsl > 0) + System.arraycopy(validSentAddr, 0, valid, 0, vsl); + if (vul > 0) + System.arraycopy(validUnsentAddr, 0, valid, vsl, vul); + validSentAddr = null; + validUnsentAddr = valid; + if (logger.isLoggable(Level.FINE)) + logger.fine("got response code " + ret + + ", with response: " + lastServerResponse); + String _lsr = lastServerResponse; // else rset will nuke it + int _lrc = lastReturnCode; + if (serverSocket != null) // hasn't already been closed + issueCommand("RSET", -1); + lastServerResponse = _lsr; // restore, for get + lastReturnCode = _lrc; + throw new SMTPSendFailedException(cmd, ret, lastServerResponse, + exception, validSentAddr, validUnsentAddr, invalidAddr); + } + } + + /** + * Send the command to the server and return the response code + * from the server. + * + * @param cmd the command + * @return the response code + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + public synchronized int simpleCommand(String cmd) + throws MessagingException { + sendCommand(cmd); + return readServerResponse(); + } + + /** + * Send the command to the server and return the response code + * from the server. + * + * @param cmd the command + * @return the response code + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + protected int simpleCommand(byte[] cmd) throws MessagingException { + assert Thread.holdsLock(this); + sendCommand(cmd); + return readServerResponse(); + } + + /** + * Sends command cmd to the server terminating + * it with CRLF. + * + * @param cmd the command + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + protected void sendCommand(String cmd) throws MessagingException { + sendCommand(toBytes(cmd)); + } + + private void sendCommand(byte[] cmdBytes) throws MessagingException { + assert Thread.holdsLock(this); + //if (logger.isLoggable(Level.FINE)) + //logger.fine("SENT: " + new String(cmdBytes, 0)); + + try { + serverOutput.write(cmdBytes); + serverOutput.write(CRLF); + serverOutput.flush(); + } catch (IOException ex) { + throw new MessagingException("Can't send command to SMTP host", ex); + } + } + + /** + * Reads server reponse returning the returnCode + * as the number. Returns -1 on failure. Sets + * lastServerResponse and lastReturnCode. + * + * @return server response code + * @exception MessagingException for failures + * @since JavaMail 1.4.1 + */ + protected int readServerResponse() throws MessagingException { + assert Thread.holdsLock(this); + String serverResponse = ""; + int returnCode = 0; + StringBuilder buf = new StringBuilder(100); + + // read the server response line(s) and add them to the buffer + // that stores the response + try { + String line = null; + + do { + line = lineInputStream.readLine(); + if (line == null) { + serverResponse = buf.toString(); + if (serverResponse.length() == 0) + serverResponse = "[EOF]"; + lastServerResponse = serverResponse; + lastReturnCode = -1; + logger.log(Level.FINE, "EOF: {0}", serverResponse); + return -1; + } + buf.append(line); + buf.append("\n"); + } while (isNotLastLine(line)); + + serverResponse = buf.toString(); + } catch (IOException ioex) { + logger.log(Level.FINE, "exception reading response", ioex); + //ioex.printStackTrace(out); + lastServerResponse = ""; + lastReturnCode = 0; + throw new MessagingException("Exception reading response", ioex); + //returnCode = -1; + } + + // print debug info + //if (logger.isLoggable(Level.FINE)) + //logger.fine("RCVD: " + serverResponse); + + // parse out the return code + if (serverResponse.length() >= 3) { + try { + returnCode = Integer.parseInt(serverResponse.substring(0, 3)); + } catch (NumberFormatException nfe) { + try { + close(); + } catch (MessagingException mex) { + // thrown by close()--ignore, will close() later anyway + logger.log(Level.FINE, "close failed", mex); + } + returnCode = -1; + } catch (StringIndexOutOfBoundsException ex) { + try { + close(); + } catch (MessagingException mex) { + // thrown by close()--ignore, will close() later anyway + logger.log(Level.FINE, "close failed", mex); + } + returnCode = -1; + } + } else { + returnCode = -1; + } + if (returnCode == -1) + logger.log(Level.FINE, "bad server response: {0}", serverResponse); + + lastServerResponse = serverResponse; + lastReturnCode = returnCode; + return returnCode; + } + + /** + * Check if we're in the connected state. Don't bother checking + * whether the server is still alive, that will be detected later. + * + * @exception IllegalStateException if not connected + * + * @since JavaMail 1.4.1 + */ + protected void checkConnected() { + if (!super.isConnected()) + throw new IllegalStateException("Not connected"); + } + + // tests if the line is an intermediate line according to SMTP + private boolean isNotLastLine(String line) { + return line != null && line.length() >= 4 && line.charAt(3) == '-'; + } + + // wraps an address in "<>"'s if necessary + private String normalizeAddress(String addr) { + if ((!addr.startsWith("<")) && (!addr.endsWith(">"))) + return "<" + addr + ">"; + else + return addr; + } + + /** + * Return true if the SMTP server supports the specified service + * extension. Extensions are reported as results of the EHLO + * command when connecting to the server. See + * RFC 1869 + * and other RFCs that define specific extensions. + * + * @param ext the service extension name + * @return true if the extension is supported + * + * @since JavaMail 1.3.2 + */ + public boolean supportsExtension(String ext) { + return extMap != null && + extMap.get(ext.toUpperCase(Locale.ENGLISH)) != null; + } + + /** + * Return the parameter the server provided for the specified + * service extension, or null if the extension isn't supported. + * + * @param ext the service extension name + * @return the extension parameter + * + * @since JavaMail 1.3.2 + */ + public String getExtensionParameter(String ext) { + return extMap == null ? null : + extMap.get(ext.toUpperCase(Locale.ENGLISH)); + } + + /** + * Does the server we're connected to support the specified + * authentication mechanism? Uses the extension information + * returned by the server from the EHLO command. + * + * @param auth the authentication mechanism + * @return true if the authentication mechanism is supported + * + * @since JavaMail 1.4.1 + */ + protected boolean supportsAuthentication(String auth) { + assert Thread.holdsLock(this); + if (extMap == null) + return false; + String a = extMap.get("AUTH"); + if (a == null) + return false; + StringTokenizer st = new StringTokenizer(a); + while (st.hasMoreTokens()) { + String tok = st.nextToken(); + if (tok.equalsIgnoreCase(auth)) + return true; + } + // hack for buggy servers that advertise capability incorrectly + if (auth.equalsIgnoreCase("LOGIN") && supportsExtension("AUTH=LOGIN")) { + logger.fine("use AUTH=LOGIN hack"); + return true; + } + return false; + } + + private static char[] hexchar = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + /** + * Convert a string to RFC 1891 xtext format. + * + *
    +     *     xtext = *( xchar / hexchar )
    +     *
    +     *     xchar = any ASCII CHAR between "!" (33) and "~" (126) inclusive,
    +     *          except for "+" and "=".
    +     *
    +     * ; "hexchar"s are intended to encode octets that cannot appear
    +     * ; as ASCII characters within an esmtp-value.
    +     *
    +     *     hexchar = ASCII "+" immediately followed by two upper case
    +     *          hexadecimal digits
    +     * 
    + * + * @param s the string to convert + * @return the xtext format string + * @since JavaMail 1.4.1 + */ + // XXX - keeping this around only for compatibility + protected static String xtext(String s) { + return xtext(s, false); + } + + /** + * Like xtext(s), but allow UTF-8 strings. + * + * @param s the string to convert + * @param utf8 convert string to UTF-8 first? + * @return the xtext format string + * @since JavaMail 1.6.0 + */ + protected static String xtext(String s, boolean utf8) { + StringBuilder sb = null; + byte[] bytes; + if (utf8) + bytes = s.getBytes(StandardCharsets.UTF_8); + else + bytes = ASCIIUtility.getBytes(s); + for (int i = 0; i < bytes.length; i++) { + char c = (char)(((int)bytes[i])&0xff); + if (!utf8 && c >= 128) // not ASCII + throw new IllegalArgumentException( + "Non-ASCII character in SMTP submitter: " + s); + if (c < '!' || c > '~' || c == '+' || c == '=') { + // not printable ASCII + if (sb == null) { + sb = new StringBuilder(s.length() + 4); + sb.append(s.substring(0, i)); + } + sb.append('+'); + sb.append(hexchar[(((int)c)& 0xf0) >> 4]); + sb.append(hexchar[((int)c)& 0x0f]); + } else { + if (sb != null) + sb.append(c); + } + } + return sb != null ? sb.toString() : s; + } + + private String traceUser(String user) { + return debugusername ? user : ""; + } + + private String tracePassword(String password) { + return debugpassword ? password : + (password == null ? "" : ""); + } + + /** + * Convert the String to either ASCII or UTF-8 bytes + * depending on allowutf8. + */ + private byte[] toBytes(String s) { + if (allowutf8) + return s.getBytes(StandardCharsets.UTF_8); + else + // don't use StandardCharsets.US_ASCII because it rejects non-ASCII + return ASCIIUtility.getBytes(s); + } + + /* + * Probe points for GlassFish monitoring. + */ + private void sendMessageStart(String subject) { } + private void sendMessageEnd() { } + + + /** + * An SMTPOutputStream that wraps a ChunkedOutputStream. + */ + private class BDATOutputStream extends SMTPOutputStream { + + /** + * Create a BDATOutputStream that wraps a ChunkedOutputStream + * of the given size and built on top of the specified + * underlying output stream. + * + * @param out the underlying output stream + * @param size the chunk size + */ + public BDATOutputStream(OutputStream out, int size) { + super(new ChunkedOutputStream(out, size)); + } + + /** + * Close this output stream. + * + * @exception IOException for I/O errors + */ + @Override + public void close() throws IOException { + out.close(); + } + } + + /** + * An OutputStream that buffers data in chunks and uses the + * RFC 3030 BDAT SMTP command to send each chunk. + */ + private class ChunkedOutputStream extends OutputStream { + private final OutputStream out; + private final byte[] buf; + private int count = 0; + + /** + * Create a ChunkedOutputStream built on top of the specified + * underlying output stream. + * + * @param out the underlying output stream + * @param size the chunk size + */ + public ChunkedOutputStream(OutputStream out, int size) { + this.out = out; + buf = new byte[size]; + } + + /** + * Writes the specified byte to this output stream. + * + * @param b the byte to write + * @exception IOException for I/O errors + */ + @Override + public void write(int b) throws IOException { + buf[count++] = (byte)b; + if (count >= buf.length) + flush(); + } + + /** + * Writes len bytes to this output stream starting at off. + * + * @param b bytes to write + * @param off offset in array + * @param len number of bytes to write + * @exception IOException for I/O errors + */ + @Override + public void write(byte b[], int off, int len) throws IOException { + while (len > 0) { + int size = Math.min(buf.length - count, len); + if (size == buf.length) { + // avoid the copy + bdat(b, off, size, false); + } else { + System.arraycopy(b, off, buf, count, size); + count += size; + } + off += size; + len -= size; + if (count >= buf.length) + flush(); + } + } + + /** + * Flush this output stream. + * + * @exception IOException for I/O errors + */ + @Override + public void flush() throws IOException { + bdat(buf, 0, count, false); + count = 0; + } + + /** + * Close this output stream. + * + * @exception IOException for I/O errors + */ + @Override + public void close() throws IOException { + bdat(buf, 0, count, true); + count = 0; + } + + /** + * Send the specified bytes using the BDAT command. + */ + private void bdat(byte[] b, int off, int len, boolean last) + throws IOException { + if (len > 0 || last) { + try { + if (last) + sendCommand("BDAT " + len + " LAST"); + else + sendCommand("BDAT " + len); + out.write(b, off, len); + out.flush(); + int ret = readServerResponse(); + if (ret != 250) + throw new IOException(lastServerResponse); + } catch (MessagingException mex) { + throw new IOException("BDAT write exception", mex); + } + } + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/SaslAuthenticator.java b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SaslAuthenticator.java new file mode 100644 index 000000000..f3486ac8e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/SaslAuthenticator.java @@ -0,0 +1,53 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.smtp; + +import javax.mail.MessagingException; + +/** + * Interface to make it easier to call SMTPSaslAuthenticator. + */ + +public interface SaslAuthenticator { + public boolean authenticate(String[] mechs, String realm, String authzid, + String u, String p) throws MessagingException; + +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/smtp/package.html b/fine-third-default/fine-mail/src/com/sun/mail/smtp/package.html new file mode 100644 index 000000000..bf97c4eb6 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/smtp/package.html @@ -0,0 +1,846 @@ + + + + + + +com.sun.mail.smtp package + + + +An SMTP protocol provider for the JavaMail API +that provides access to an SMTP server. +Refer to RFC 821 +for more information. +

    +When sending a message, detailed information on each address that +fails is available in an +{@link com.sun.mail.smtp.SMTPAddressFailedException SMTPAddressFailedException} +chained off the top level +{@link javax.mail.SendFailedException SendFailedException} +that is thrown. +In addition, if the mail.smtp.reportsuccess property +is set, an +{@link com.sun.mail.smtp.SMTPAddressSucceededException +SMTPAddressSucceededException} +will be included in the list for each address that is successful. +Note that this will cause a top level +{@link javax.mail.SendFailedException SendFailedException} +to be thrown even though the send was successful. +

    +

    +The SMTP provider also supports ESMTP +(RFC 1651). +It can optionally use SMTP Authentication +(RFC 2554) +using the LOGIN, PLAIN, DIGEST-MD5, and NTLM mechanisms +(RFC 4616 +and RFC 2831). +

    +

    +To use SMTP authentication you'll need to set the mail.smtp.auth +property (see below) or provide the SMTP Transport +with a username and password when connecting to the SMTP server. You +can do this using one of the following approaches: +

    +
      +
    • +

      +Provide an Authenticator object when creating your mail Session +and provide the username and password information during the +Authenticator callback. +

      +

      +Note that the mail.smtp.user property can be set to provide a +default username for the callback, but the password will still need to be +supplied explicitly. +

      +

      +This approach allows you to use the static Transport send method +to send messages. +

      +
    • +
    • +

      +Call the Transport connect method explicitly with username and +password arguments. +

      +

      +This approach requires you to explicitly manage a Transport object +and use the Transport sendMessage method to send the message. +The transport.java demo program demonstrates how to manage a Transport +object. The following is roughly equivalent to the static +Transport send method, but supplies the needed username and +password: +

      +
      +Transport tr = session.getTransport("smtp");
      +tr.connect(smtphost, username, password);
      +msg.saveChanges();	// don't forget this
      +tr.sendMessage(msg, msg.getAllRecipients());
      +tr.close();
      +
      +
    • +
    +

    +When using DIGEST-MD5 authentication, +you'll also need to supply an appropriate realm; +your mail server administrator can supply this information. +You can set this using the mail.smtp.sasl.realm property, +or the setSASLRealm method on SMTPTransport. +

    +

    +The SMTP protocol provider can use SASL +(RFC 2222) +authentication mechanisms on systems that support the +javax.security.sasl APIs, such as J2SE 5.0. +In addition to the SASL mechanisms that are built into +the SASL implementation, users can also provide additional +SASL mechanisms of their own design to support custom authentication +schemes. See the + +Java SASL API Programming and Deployment Guide for details. +Note that the current implementation doesn't support SASL mechanisms +that provide their own integrity or confidentiality layer. +

    +

    +Support for OAuth 2.0 authentication via the + +XOAUTH2 authentication mechanism is provided either through the SASL +support described above or as a built-in authentication mechanism in the +SMTP provider. +The OAuth 2.0 Access Token should be passed as the password for this mechanism. +See +OAuth2 Support for details. +

    +

    +SMTP can also optionally request Delivery Status Notifications +(RFC 1891). +The delivery status will typically be reported using +a "multipart/report" +(RFC 1892) +message type with a "message/delivery-status" +(RFC 1894) +part. +You can use the classes in the com.sun.mail.dsn package to +handle these MIME types. +Note that you'll need to include dsn.jar in your CLASSPATH +as this support is not included in mail.jar. +

    +

    +See below for the properties to enable these features. +

    +

    +Note also that THERE IS NOT SUFFICIENT DOCUMENTATION HERE TO USE THESE +FEATURES!!! You will need to read the appropriate RFCs mentioned above +to understand what these features do and how to use them. Don't just +start setting properties and then complain to us when it doesn't work +like you expect it to work. READ THE RFCs FIRST!!! +

    +

    +The SMTP protocol provider supports the CHUNKING extension defined in +RFC 3030. +Set the mail.smtp.chunksize property to the desired chunk +size in bytes. +If the server supports the CHUNKING extension, the BDAT command will be +used to send the message in chunksize pieces. Note that no pipelining is +done so this will be slower than sending the message in one piece. +Note also that the BINARYMIME extension described in RFC 3030 is NOT supported. +

    +Properties +

    +The SMTP protocol provider supports the following properties, +which may be set in the JavaMail Session object. +The properties are always set as strings; the Type column describes +how the string is interpreted. For example, use +

    +
    +	props.put("mail.smtp.port", "888");
    +
    +

    +to set the mail.smtp.port property, which is of type int. +

    +

    +Note that if you're using the "smtps" protocol to access SMTP over SSL, +all the properties would be named "mail.smtps.*". +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mail.smtp.userStringDefault user name for SMTP.
    mail.smtp.hostStringThe SMTP server to connect to.
    mail.smtp.portintThe SMTP server port to connect to, if the connect() method doesn't +explicitly specify one. Defaults to 25.
    mail.smtp.connectiontimeoutintSocket connection timeout value in milliseconds. +This timeout is implemented by java.net.Socket. +Default is infinite timeout.
    mail.smtp.timeoutintSocket read timeout value in milliseconds. +This timeout is implemented by java.net.Socket. +Default is infinite timeout.
    mail.smtp.writetimeoutintSocket write timeout value in milliseconds. +This timeout is implemented by using a +java.util.concurrent.ScheduledExecutorService per connection +that schedules a thread to close the socket if the timeout expires. +Thus, the overhead of using this timeout is one thread per connection. +Default is infinite timeout.
    mail.smtp.fromString +Email address to use for SMTP MAIL command. This sets the envelope +return address. Defaults to msg.getFrom() or +InternetAddress.getLocalAddress(). NOTE: mail.smtp.user was previously +used for this. +
    mail.smtp.localhostString +Local host name used in the SMTP HELO or EHLO command. +Defaults to InetAddress.getLocalHost().getHostName(). +Should not normally need to +be set if your JDK and your name service are configured properly. +
    mail.smtp.localaddressString +Local address (host name) to bind to when creating the SMTP socket. +Defaults to the address picked by the Socket class. +Should not normally need to be set, but useful with multi-homed hosts +where it's important to pick a particular local address to bind to. +
    mail.smtp.localportint +Local port number to bind to when creating the SMTP socket. +Defaults to the port number picked by the Socket class. +
    mail.smtp.ehloboolean +If false, do not attempt to sign on with the EHLO command. Defaults to +true. Normally failure of the EHLO command will fallback to the HELO +command; this property exists only for servers that don't fail EHLO +properly or don't implement EHLO properly. +
    mail.smtp.authbooleanIf true, attempt to authenticate the user using the AUTH command. +Defaults to false.
    mail.smtp.auth.mechanismsString +If set, lists the authentication mechanisms to consider, and the order +in which to consider them. Only mechanisms supported by the server and +supported by the current implementation will be used. +The default is "LOGIN PLAIN DIGEST-MD5 NTLM", which includes all +the authentication mechanisms supported by the current implementation +except XOAUTH2. +
    mail.smtp.auth.login.disablebooleanIf true, prevents use of the AUTH LOGIN command. +Default is false.
    mail.smtp.auth.plain.disablebooleanIf true, prevents use of the AUTH PLAIN command. +Default is false.
    mail.smtp.auth.digest-md5.disablebooleanIf true, prevents use of the AUTH DIGEST-MD5 command. +Default is false.
    mail.smtp.auth.ntlm.disablebooleanIf true, prevents use of the AUTH NTLM command. +Default is false.
    mail.smtp.auth.ntlm.domainString +The NTLM authentication domain. +
    mail.smtp.auth.ntlm.flagsint +NTLM protocol-specific flags. +See +http://curl.haxx.se/rfc/ntlm.html#theNtlmFlags for details. +
    mail.smtp.auth.xoauth2.disablebooleanIf true, prevents use of the AUTHENTICATE XOAUTH2 command. +Because the OAuth 2.0 protocol requires a special access token instead of +a password, this mechanism is disabled by default. Enable it by explicitly +setting this property to "false" or by setting the "mail.smtp.auth.mechanisms" +property to "XOAUTH2".
    mail.smtp.submitterStringThe submitter to use in the AUTH tag in the MAIL FROM command. +Typically used by a mail relay to pass along information about the +original submitter of the message. +See also the {@link com.sun.mail.smtp.SMTPMessage#setSubmitter setSubmitter} +method of {@link com.sun.mail.smtp.SMTPMessage SMTPMessage}. +Mail clients typically do not use this. +
    mail.smtp.dsn.notifyStringThe NOTIFY option to the RCPT command. Either NEVER, or some +combination of SUCCESS, FAILURE, and DELAY (separated by commas).
    mail.smtp.dsn.retStringThe RET option to the MAIL command. Either FULL or HDRS.
    mail.smtp.allow8bitmimeboolean +If set to true, and the server supports the 8BITMIME extension, text +parts of messages that use the "quoted-printable" or "base64" encodings +are converted to use "8bit" encoding if they follow the RFC2045 rules +for 8bit text. +
    mail.smtp.sendpartialboolean +If set to true, and a message has some valid and some invalid +addresses, send the message anyway, reporting the partial failure with +a SendFailedException. If set to false (the default), the message is +not sent to any of the recipients if there is an invalid recipient +address. +
    mail.smtp.sasl.enableboolean +If set to true, attempt to use the javax.security.sasl package to +choose an authentication mechanism for login. +Defaults to false. +
    mail.smtp.sasl.mechanismsString +A space or comma separated list of SASL mechanism names to try +to use. +
    mail.smtp.sasl.authorizationidString +The authorization ID to use in the SASL authentication. +If not set, the authentication ID (user name) is used. +
    mail.smtp.sasl.realmStringThe realm to use with DIGEST-MD5 authentication.
    mail.smtp.sasl.usecanonicalhostnameboolean +If set to true, the canonical host name returned by +{@link java.net.InetAddress#getCanonicalHostName InetAddress.getCanonicalHostName} +is passed to the SASL mechanism, instead of the host name used to connect. +Defaults to false. +
    mail.smtp.quitwaitboolean +If set to false, the QUIT command is sent +and the connection is immediately closed. +If set to true (the default), causes the transport to wait +for the response to the QUIT command. +
    mail.smtp.reportsuccessboolean +If set to true, causes the transport to include an +{@link com.sun.mail.smtp.SMTPAddressSucceededException +SMTPAddressSucceededException} +for each address that is successful. +Note also that this will cause a +{@link javax.mail.SendFailedException SendFailedException} +to be thrown from the +{@link com.sun.mail.smtp.SMTPTransport#sendMessage sendMessage} +method of +{@link com.sun.mail.smtp.SMTPTransport SMTPTransport} +even if all addresses were correct and the message was sent +successfully. +
    mail.smtp.socketFactorySocketFactory +If set to a class that implements the +javax.net.SocketFactory interface, this class +will be used to create SMTP sockets. Note that this is an +instance of a class, not a name, and must be set using the +put method, not the setProperty method. +
    mail.smtp.socketFactory.classString +If set, specifies the name of a class that implements the +javax.net.SocketFactory interface. This class +will be used to create SMTP sockets. +
    mail.smtp.socketFactory.fallbackboolean +If set to true, failure to create a socket using the specified +socket factory class will cause the socket to be created using +the java.net.Socket class. +Defaults to true. +
    mail.smtp.socketFactory.portint +Specifies the port to connect to when using the specified socket +factory. +If not set, the default port will be used. +
    mail.smtp.ssl.enableboolean +If set to true, use SSL to connect and use the SSL port by default. +Defaults to false for the "smtp" protocol and true for the "smtps" protocol. +
    mail.smtp.ssl.checkserveridentityboolean +If set to true, check the server identity as specified by +RFC 2595. +These additional checks based on the content of the server's certificate +are intended to prevent man-in-the-middle attacks. +Defaults to false. +
    mail.smtp.ssl.trustString +If set, and a socket factory hasn't been specified, enables use of a +{@link com.sun.mail.util.MailSSLSocketFactory MailSSLSocketFactory}. +If set to "*", all hosts are trusted. +If set to a whitespace separated list of hosts, those hosts are trusted. +Otherwise, trust depends on the certificate the server presents. +
    mail.smtp.ssl.socketFactorySSLSocketFactory +If set to a class that extends the +javax.net.ssl.SSLSocketFactory class, this class +will be used to create SMTP SSL sockets. Note that this is an +instance of a class, not a name, and must be set using the +put method, not the setProperty method. +
    mail.smtp.ssl.socketFactory.classString +If set, specifies the name of a class that extends the +javax.net.ssl.SSLSocketFactory class. This class +will be used to create SMTP SSL sockets. +
    mail.smtp.ssl.socketFactory.portint +Specifies the port to connect to when using the specified socket +factory. +If not set, the default port will be used. +
    mail.smtp.ssl.protocolsstring +Specifies the SSL protocols that will be enabled for SSL connections. +The property value is a whitespace separated list of tokens acceptable +to the javax.net.ssl.SSLSocket.setEnabledProtocols method. +
    mail.smtp.ssl.ciphersuitesstring +Specifies the SSL cipher suites that will be enabled for SSL connections. +The property value is a whitespace separated list of tokens acceptable +to the javax.net.ssl.SSLSocket.setEnabledCipherSuites method. +
    mail.smtp.starttls.enableboolean +If true, enables the use of the STARTTLS command (if +supported by the server) to switch the connection to a TLS-protected +connection before issuing any login commands. +If the server does not support STARTTLS, the connection continues without +the use of TLS; see the +mail.smtp.starttls.required +property to fail if STARTTLS isn't supported. +Note that an appropriate trust store must configured so that the client +will trust the server's certificate. +Defaults to false. +
    mail.smtp.starttls.requiredboolean +If true, requires the use of the STARTTLS command. +If the server doesn't support the STARTTLS command, or the command +fails, the connect method will fail. +Defaults to false. +
    mail.smtp.proxy.hoststring +Specifies the host name of an HTTP web proxy server that will be used for +connections to the mail server. +
    mail.smtp.proxy.portstring +Specifies the port number for the HTTP web proxy server. +Defaults to port 80. +
    mail.smtp.proxy.userstring +Specifies the user name to use to authenticate with the HTTP web proxy server. +By default, no authentication is done. +
    mail.smtp.proxy.passwordstring +Specifies the password to use to authenticate with the HTTP web proxy server. +By default, no authentication is done. +
    mail.smtp.socks.hoststring +Specifies the host name of a SOCKS5 proxy server that will be used for +connections to the mail server. +
    mail.smtp.socks.portstring +Specifies the port number for the SOCKS5 proxy server. +This should only need to be used if the proxy server is not using +the standard port number of 1080. +
    mail.smtp.mailextensionString +Extension string to append to the MAIL command. +The extension string can be used to specify standard SMTP +service extensions as well as vendor-specific extensions. +Typically the application should use the +{@link com.sun.mail.smtp.SMTPTransport SMTPTransport} +method {@link com.sun.mail.smtp.SMTPTransport#supportsExtension +supportsExtension} +to verify that the server supports the desired service extension. +See RFC 1869 +and other RFCs that define specific extensions. +
    mail.smtp.usersetboolean +If set to true, use the RSET command instead of the NOOP command +in the {@link javax.mail.Transport#isConnected isConnected} method. +In some cases sendmail will respond slowly after many NOOP commands; +use of RSET avoids this sendmail issue. +Defaults to false. +
    mail.smtp.noop.strictboolean +If set to true (the default), insist on a 250 response code from the NOOP +command to indicate success. The NOOP command is used by the +{@link javax.mail.Transport#isConnected isConnected} method to determine +if the connection is still alive. +Some older servers return the wrong response code on success, some +servers don't implement the NOOP command at all and so always return +a failure code. Set this property to false to handle servers +that are broken in this way. +Normally, when a server times out a connection, it will send a 421 +response code, which the client will see as the response to the next +command it issues. +Some servers send the wrong failure response code when timing out a +connection. +Do not set this property to false when dealing with servers that are +broken in this way. +
    +

    +In general, applications should not need to use the classes in this +package directly. Instead, they should use the APIs defined by +javax.mail package (and subpackages). Applications should +never construct instances of SMTPTransport directly. +Instead, they should use the +Session method getTransport to acquire an +appropriate Transport object. +

    +

    +In addition to printing debugging output as controlled by the +{@link javax.mail.Session Session} configuration, +the com.sun.mail.smtp provider logs the same information using +{@link java.util.logging.Logger} as described in the following table: +

    + + + + + + + + + + + + + + + + + + + + + + + + +
    Logger NameLogging LevelPurpose
    com.sun.mail.smtpCONFIGConfiguration of the SMTPTransport
    com.sun.mail.smtpFINEGeneral debugging output
    com.sun.mail.smtp.protocolFINESTComplete protocol trace
    +

    +WARNING: The APIs unique to this package should be +considered EXPERIMENTAL. They may be changed in the +future in ways that are incompatible with applications using the +current APIs. +

    + + + diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/ASCIIUtility.java b/fine-third-default/fine-mail/src/com/sun/mail/util/ASCIIUtility.java new file mode 100644 index 000000000..1c050a9c3 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/ASCIIUtility.java @@ -0,0 +1,308 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.IOException; + +public class ASCIIUtility { + + // Private constructor so that this class is not instantiated + private ASCIIUtility() { } + + /** + * Convert the bytes within the specified range of the given byte + * array into a signed integer in the given radix . The range extends + * from start till, but not including end.

    + * + * Based on java.lang.Integer.parseInt() + * + * @param b the bytes + * @param start the first byte offset + * @param end the last byte offset + * @param radix the radix + * @return the integer value + * @exception NumberFormatException for conversion errors + */ + public static int parseInt(byte[] b, int start, int end, int radix) + throws NumberFormatException { + if (b == null) + throw new NumberFormatException("null"); + + int result = 0; + boolean negative = false; + int i = start; + int limit; + int multmin; + int digit; + + if (end > start) { + if (b[i] == '-') { + negative = true; + limit = Integer.MIN_VALUE; + i++; + } else { + limit = -Integer.MAX_VALUE; + } + multmin = limit / radix; + if (i < end) { + digit = Character.digit((char)b[i++], radix); + if (digit < 0) { + throw new NumberFormatException( + "illegal number: " + toString(b, start, end) + ); + } else { + result = -digit; + } + } + while (i < end) { + // Accumulating negatively avoids surprises near MAX_VALUE + digit = Character.digit((char)b[i++], radix); + if (digit < 0) { + throw new NumberFormatException("illegal number"); + } + if (result < multmin) { + throw new NumberFormatException("illegal number"); + } + result *= radix; + if (result < limit + digit) { + throw new NumberFormatException("illegal number"); + } + result -= digit; + } + } else { + throw new NumberFormatException("illegal number"); + } + if (negative) { + if (i > start + 1) { + return result; + } else { /* Only got "-" */ + throw new NumberFormatException("illegal number"); + } + } else { + return -result; + } + } + + /** + * Convert the bytes within the specified range of the given byte + * array into a signed integer . The range extends from + * start till, but not including end. + * + * @param b the bytes + * @param start the first byte offset + * @param end the last byte offset + * @return the integer value + * @exception NumberFormatException for conversion errors + */ + public static int parseInt(byte[] b, int start, int end) + throws NumberFormatException { + return parseInt(b, start, end, 10); + } + + /** + * Convert the bytes within the specified range of the given byte + * array into a signed long in the given radix . The range extends + * from start till, but not including end.

    + * + * Based on java.lang.Long.parseLong() + * + * @param b the bytes + * @param start the first byte offset + * @param end the last byte offset + * @param radix the radix + * @return the long value + * @exception NumberFormatException for conversion errors + */ + public static long parseLong(byte[] b, int start, int end, int radix) + throws NumberFormatException { + if (b == null) + throw new NumberFormatException("null"); + + long result = 0; + boolean negative = false; + int i = start; + long limit; + long multmin; + int digit; + + if (end > start) { + if (b[i] == '-') { + negative = true; + limit = Long.MIN_VALUE; + i++; + } else { + limit = -Long.MAX_VALUE; + } + multmin = limit / radix; + if (i < end) { + digit = Character.digit((char)b[i++], radix); + if (digit < 0) { + throw new NumberFormatException( + "illegal number: " + toString(b, start, end) + ); + } else { + result = -digit; + } + } + while (i < end) { + // Accumulating negatively avoids surprises near MAX_VALUE + digit = Character.digit((char)b[i++], radix); + if (digit < 0) { + throw new NumberFormatException("illegal number"); + } + if (result < multmin) { + throw new NumberFormatException("illegal number"); + } + result *= radix; + if (result < limit + digit) { + throw new NumberFormatException("illegal number"); + } + result -= digit; + } + } else { + throw new NumberFormatException("illegal number"); + } + if (negative) { + if (i > start + 1) { + return result; + } else { /* Only got "-" */ + throw new NumberFormatException("illegal number"); + } + } else { + return -result; + } + } + + /** + * Convert the bytes within the specified range of the given byte + * array into a signed long . The range extends from + * start till, but not including end.

    + * + * @param b the bytes + * @param start the first byte offset + * @param end the last byte offset + * @return the long value + * @exception NumberFormatException for conversion errors + */ + public static long parseLong(byte[] b, int start, int end) + throws NumberFormatException { + return parseLong(b, start, end, 10); + } + + /** + * Convert the bytes within the specified range of the given byte + * array into a String. The range extends from start + * till, but not including end. + * + * @param b the bytes + * @param start the first byte offset + * @param end the last byte offset + * @return the String + */ + public static String toString(byte[] b, int start, int end) { + int size = end - start; + char[] theChars = new char[size]; + + for (int i = 0, j = start; i < size; ) + theChars[i++] = (char)(b[j++]&0xff); + + return new String(theChars); + } + + /** + * Convert the bytes into a String. + * + * @param b the bytes + * @return the String + * @since JavaMail 1.4.4 + */ + public static String toString(byte[] b) { + return toString(b, 0, b.length); + } + + public static String toString(ByteArrayInputStream is) { + int size = is.available(); + char[] theChars = new char[size]; + byte[] bytes = new byte[size]; + + is.read(bytes, 0, size); + for (int i = 0; i < size;) + theChars[i] = (char)(bytes[i++]&0xff); + + return new String(theChars); + } + + + public static byte[] getBytes(String s) { + char [] chars= s.toCharArray(); + int size = chars.length; + byte[] bytes = new byte[size]; + + for (int i = 0; i < size;) + bytes[i] = (byte) chars[i++]; + return bytes; + } + + public static byte[] getBytes(InputStream is) throws IOException { + + int len; + int size = 1024; + byte [] buf; + + + if (is instanceof ByteArrayInputStream) { + size = is.available(); + buf = new byte[size]; + len = is.read(buf, 0, size); + } + else { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + buf = new byte[size]; + while ((len = is.read(buf, 0, size)) != -1) + bos.write(buf, 0, len); + buf = bos.toByteArray(); + } + return buf; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/BASE64DecoderStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/BASE64DecoderStream.java new file mode 100644 index 000000000..8a65b6e56 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/BASE64DecoderStream.java @@ -0,0 +1,478 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; + +/** + * This class implements a BASE64 Decoder. It is implemented as + * a FilterInputStream, so one can just wrap this class around + * any input stream and read bytes from this filter. The decoding + * is done as the bytes are read out. + * + * @author John Mani + * @author Bill Shannon + */ + +public class BASE64DecoderStream extends FilterInputStream { + // buffer of decoded bytes for single byte reads + private byte[] buffer = new byte[3]; + private int bufsize = 0; // size of the cache + private int index = 0; // index into the cache + + // buffer for almost 8K of typical 76 chars + CRLF lines, + // used by getByte method. this buffer contains encoded bytes. + private byte[] input_buffer = new byte[78*105]; + private int input_pos = 0; + private int input_len = 0;; + + private boolean ignoreErrors = false; + + /** + * Create a BASE64 decoder that decodes the specified input stream. + * The System property mail.mime.base64.ignoreerrors + * controls whether errors in the encoded data cause an exception + * or are ignored. The default is false (errors cause exception). + * + * @param in the input stream + */ + public BASE64DecoderStream(InputStream in) { + super(in); + // default to false + ignoreErrors = PropUtil.getBooleanSystemProperty( + "mail.mime.base64.ignoreerrors", false); + } + + /** + * Create a BASE64 decoder that decodes the specified input stream. + * + * @param in the input stream + * @param ignoreErrors ignore errors in encoded data? + */ + public BASE64DecoderStream(InputStream in, boolean ignoreErrors) { + super(in); + this.ignoreErrors = ignoreErrors; + } + + /** + * Read the next decoded byte from this input stream. The byte + * is returned as an int in the range 0 + * to 255. If no byte is available because the end of + * the stream has been reached, the value -1 is returned. + * This method blocks until input data is available, the end of the + * stream is detected, or an exception is thrown. + * + * @return next byte of data, or -1 if the end of the + * stream is reached. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterInputStream#in + */ + @Override + public int read() throws IOException { + if (index >= bufsize) { + bufsize = decode(buffer, 0, buffer.length); + if (bufsize <= 0) // buffer is empty + return -1; + index = 0; // reset index into buffer + } + return buffer[index++] & 0xff; // Zero off the MSB + } + + /** + * Reads up to len decoded bytes of data from this input stream + * into an array of bytes. This method blocks until some input is + * available. + *

    + * + * @param buf the buffer into which the data is read. + * @param off the start offset of the data. + * @param len the maximum number of bytes read. + * @return the total number of bytes read into the buffer, or + * -1 if there is no more data because the end of + * the stream has been reached. + * @exception IOException if an I/O error occurs. + */ + @Override + public int read(byte[] buf, int off, int len) throws IOException { + // empty out single byte read buffer + int off0 = off; + while (index < bufsize && len > 0) { + buf[off++] = buffer[index++]; + len--; + } + if (index >= bufsize) + bufsize = index = 0; + + int bsize = (len / 3) * 3; // round down to multiple of 3 bytes + if (bsize > 0) { + int size = decode(buf, off, bsize); + off += size; + len -= size; + + if (size != bsize) { // hit EOF? + if (off == off0) // haven't returned any data + return -1; + else // returned some data before hitting EOF + return off - off0; + } + } + + // finish up with a partial read if necessary + for (; len > 0; len--) { + int c = read(); + if (c == -1) // EOF + break; + buf[off++] = (byte)c; + } + + if (off == off0) // haven't returned any data + return -1; + else // returned some data before hitting EOF + return off - off0; + } + + /** + * Skips over and discards n bytes of data from this stream. + */ + @Override + public long skip(long n) throws IOException { + long skipped = 0; + while (n-- > 0 && read() >= 0) + skipped++; + return skipped; + } + + /** + * Tests if this input stream supports marks. Currently this class + * does not support marks + */ + @Override + public boolean markSupported() { + return false; // Maybe later .. + } + + /** + * Returns the number of bytes that can be read from this input + * stream without blocking. However, this figure is only + * a close approximation in case the original encoded stream + * contains embedded CRLFs; since the CRLFs are discarded, not decoded + */ + @Override + public int available() throws IOException { + // This is only an estimate, since in.available() + // might include CRLFs too .. + return ((in.available() * 3)/4 + (bufsize-index)); + } + + /** + * This character array provides the character to value map + * based on RFC1521. + */ + private final static char pem_array[] = { + 'A','B','C','D','E','F','G','H', // 0 + 'I','J','K','L','M','N','O','P', // 1 + 'Q','R','S','T','U','V','W','X', // 2 + 'Y','Z','a','b','c','d','e','f', // 3 + 'g','h','i','j','k','l','m','n', // 4 + 'o','p','q','r','s','t','u','v', // 5 + 'w','x','y','z','0','1','2','3', // 6 + '4','5','6','7','8','9','+','/' // 7 + }; + + private final static byte pem_convert_array[] = new byte[256]; + + static { + for (int i = 0; i < 255; i++) + pem_convert_array[i] = -1; + for (int i = 0; i < pem_array.length; i++) + pem_convert_array[pem_array[i]] = (byte)i; + } + + /** + * The decoder algorithm. Most of the complexity here is dealing + * with error cases. Returns the number of bytes decoded, which + * may be zero. Decoding is done by filling an int with 4 6-bit + * values by shifting them in from the bottom and then extracting + * 3 8-bit bytes from the int by shifting them out from the bottom. + * + * @param outbuf the buffer into which to put the decoded bytes + * @param pos position in the buffer to start filling + * @param len the number of bytes to fill + * @return the number of bytes filled, always a multiple + * of three, and may be zero + * @exception IOException if the data is incorrectly formatted + */ + private int decode(byte[] outbuf, int pos, int len) throws IOException { + int pos0 = pos; + while (len >= 3) { + /* + * We need 4 valid base64 characters before we start decoding. + * We skip anything that's not a valid base64 character (usually + * just CRLF). + */ + int got = 0; + int val = 0; + while (got < 4) { + int i = getByte(); + if (i == -1 || i == -2) { + boolean atEOF; + if (i == -1) { + if (got == 0) + return pos - pos0; + if (!ignoreErrors) + throw new DecodingException( + "BASE64Decoder: Error in encoded stream: " + + "needed 4 valid base64 characters " + + "but only got " + got + " before EOF" + + recentChars()); + atEOF = true; // don't read any more + } else { // i == -2 + // found a padding character, we're at EOF + // XXX - should do something to make EOF "sticky" + if (got < 2 && !ignoreErrors) + throw new DecodingException( + "BASE64Decoder: Error in encoded stream: " + + "needed at least 2 valid base64 characters," + + " but only got " + got + + " before padding character (=)" + + recentChars()); + + // didn't get any characters before padding character? + if (got == 0) + return pos - pos0; + atEOF = false; // need to keep reading + } + + // pad partial result with zeroes + + // how many bytes will we produce on output? + // (got always < 4, so size always < 3) + int size = got - 1; + if (size == 0) + size = 1; + + // handle the one padding character we've seen + got++; + val <<= 6; + + while (got < 4) { + if (!atEOF) { + // consume the rest of the padding characters, + // filling with zeroes + i = getByte(); + if (i == -1) { + if (!ignoreErrors) + throw new DecodingException( + "BASE64Decoder: Error in encoded " + + "stream: hit EOF while looking for " + + "padding characters (=)" + + recentChars()); + } else if (i != -2) { + if (!ignoreErrors) + throw new DecodingException( + "BASE64Decoder: Error in encoded " + + "stream: found valid base64 " + + "character after a padding character " + + "(=)" + recentChars()); + } + } + val <<= 6; + got++; + } + + // now pull out however many valid bytes we got + val >>= 8; // always skip first one + if (size == 2) + outbuf[pos + 1] = (byte)(val & 0xff); + val >>= 8; + outbuf[pos] = (byte)(val & 0xff); + // len -= size; // not needed, return below + pos += size; + return pos - pos0; + } else { + // got a valid byte + val <<= 6; + got++; + val |= i; + } + } + + // read 4 valid characters, now extract 3 bytes + outbuf[pos + 2] = (byte)(val & 0xff); + val >>= 8; + outbuf[pos + 1] = (byte)(val & 0xff); + val >>= 8; + outbuf[pos] = (byte)(val & 0xff); + len -= 3; + pos += 3; + } + return pos - pos0; + } + + /** + * Read the next valid byte from the input stream. + * Buffer lots of data from underlying stream in input_buffer, + * for efficiency. + * + * @return the next byte, -1 on EOF, or -2 if next byte is '=' + * (padding at end of encoded data) + */ + private int getByte() throws IOException { + int c; + do { + if (input_pos >= input_len) { + try { + input_len = in.read(input_buffer); + } catch (EOFException ex) { + return -1; + } + if (input_len <= 0) + return -1; + input_pos = 0; + } + // get the next byte in the buffer + c = input_buffer[input_pos++] & 0xff; + // is it a padding byte? + if (c == '=') + return -2; + // no, convert it + c = pem_convert_array[c]; + // loop until we get a legitimate byte + } while (c == -1); + return c; + } + + /** + * Return the most recent characters, for use in an error message. + */ + private String recentChars() { + // reach into the input buffer and extract up to 10 + // recent characters, to help in debugging. + String errstr = ""; + int nc = input_pos > 10 ? 10 : input_pos; + if (nc > 0) { + errstr += ", the " + nc + + " most recent characters were: \""; + for (int k = input_pos - nc; k < input_pos; k++) { + char c = (char)(input_buffer[k] & 0xff); + switch (c) { + case '\r': errstr += "\\r"; break; + case '\n': errstr += "\\n"; break; + case '\t': errstr += "\\t"; break; + default: + if (c >= ' ' && c < 0177) + errstr += c; + else + errstr += ("\\" + (int)c); + } + } + errstr += "\""; + } + return errstr; + } + + /** + * Base64 decode a byte array. No line breaks are allowed. + * This method is suitable for short strings, such as those + * in the IMAP AUTHENTICATE protocol, but not to decode the + * entire content of a MIME part. + * + * NOTE: inbuf may only contain valid base64 characters. + * Whitespace is not ignored. + * + * @param inbuf the byte array + * @return the decoded byte array + */ + public static byte[] decode(byte[] inbuf) { + int size = (inbuf.length / 4) * 3; + if (size == 0) + return inbuf; + + if (inbuf[inbuf.length - 1] == '=') { + size--; + if (inbuf[inbuf.length - 2] == '=') + size--; + } + byte[] outbuf = new byte[size]; + + int inpos = 0, outpos = 0; + size = inbuf.length; + while (size > 0) { + int val; + int osize = 3; + val = pem_convert_array[inbuf[inpos++] & 0xff]; + val <<= 6; + val |= pem_convert_array[inbuf[inpos++] & 0xff]; + val <<= 6; + if (inbuf[inpos] != '=') // End of this BASE64 encoding + val |= pem_convert_array[inbuf[inpos++] & 0xff]; + else + osize--; + val <<= 6; + if (inbuf[inpos] != '=') // End of this BASE64 encoding + val |= pem_convert_array[inbuf[inpos++] & 0xff]; + else + osize--; + if (osize > 2) + outbuf[outpos + 2] = (byte)(val & 0xff); + val >>= 8; + if (osize > 1) + outbuf[outpos + 1] = (byte)(val & 0xff); + val >>= 8; + outbuf[outpos] = (byte)(val & 0xff); + outpos += osize; + size -= 4; + } + return outbuf; + } + + /*** begin TEST program *** + public static void main(String argv[]) throws Exception { + FileInputStream infile = new FileInputStream(argv[0]); + BASE64DecoderStream decoder = new BASE64DecoderStream(infile); + int c; + + while ((c = decoder.read()) != -1) + System.out.print((char)c); + System.out.flush(); + } + *** end TEST program ***/ +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/BASE64EncoderStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/BASE64EncoderStream.java new file mode 100644 index 000000000..f17862ec0 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/BASE64EncoderStream.java @@ -0,0 +1,330 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; + +/** + * This class implements a BASE64 encoder. It is implemented as + * a FilterOutputStream, so one can just wrap this class around + * any output stream and write bytes into this filter. The encoding + * is done as the bytes are written out. + * + * @author John Mani + * @author Bill Shannon + */ + +public class BASE64EncoderStream extends FilterOutputStream { + private byte[] buffer; // cache of bytes that are yet to be encoded + private int bufsize = 0; // size of the cache + private byte[] outbuf; // line size output buffer + private int count = 0; // number of bytes that have been output + private int bytesPerLine; // number of bytes per line + private int lineLimit; // number of input bytes to output bytesPerLine + private boolean noCRLF = false; + + private static byte[] newline = new byte[] { '\r', '\n' }; + + /** + * Create a BASE64 encoder that encodes the specified output stream. + * + * @param out the output stream + * @param bytesPerLine number of bytes per line. The encoder inserts + * a CRLF sequence after the specified number of bytes, + * unless bytesPerLine is Integer.MAX_VALUE, in which + * case no CRLF is inserted. bytesPerLine is rounded + * down to a multiple of 4. + */ + public BASE64EncoderStream(OutputStream out, int bytesPerLine) { + super(out); + buffer = new byte[3]; + if (bytesPerLine == Integer.MAX_VALUE || bytesPerLine < 4) { + noCRLF = true; + bytesPerLine = 76; + } + bytesPerLine = (bytesPerLine / 4) * 4; // Rounded down to 4n + this.bytesPerLine = bytesPerLine; // save it + lineLimit = bytesPerLine / 4 * 3; + + if (noCRLF) { + outbuf = new byte[bytesPerLine]; + } else { + outbuf = new byte[bytesPerLine + 2]; + outbuf[bytesPerLine] = (byte)'\r'; + outbuf[bytesPerLine + 1] = (byte)'\n'; + } + } + + /** + * Create a BASE64 encoder that encodes the specified input stream. + * Inserts the CRLF sequence after outputting 76 bytes. + * + * @param out the output stream + */ + public BASE64EncoderStream(OutputStream out) { + this(out, 76); + } + + /** + * Encodes len bytes from the specified + * byte array starting at offset off to + * this output stream. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @exception IOException if an I/O error occurs. + */ + @Override + public synchronized void write(byte[] b, int off, int len) + throws IOException { + int end = off + len; + + // finish off incomplete coding unit + while (bufsize != 0 && off < end) + write(b[off++]); + + // finish off line + int blen = ((bytesPerLine - count) / 4) * 3; + if (off + blen <= end) { + // number of bytes that will be produced in outbuf + int outlen = encodedSize(blen); + if (!noCRLF) { + outbuf[outlen++] = (byte)'\r'; + outbuf[outlen++] = (byte)'\n'; + } + out.write(encode(b, off, blen, outbuf), 0, outlen); + off += blen; + count = 0; + } + + // do bulk encoding a line at a time. + for (; off + lineLimit <= end; off += lineLimit) + out.write(encode(b, off, lineLimit, outbuf)); + + // handle remaining partial line + if (off + 3 <= end) { + blen = end - off; + blen = (blen / 3) * 3; // round down + // number of bytes that will be produced in outbuf + int outlen = encodedSize(blen); + out.write(encode(b, off, blen, outbuf), 0, outlen); + off += blen; + count += outlen; + } + + // start next coding unit + for (; off < end; off++) + write(b[off]); + } + + /** + * Encodes b.length bytes to this output stream. + * + * @param b the data to be written. + * @exception IOException if an I/O error occurs. + */ + @Override + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + /** + * Encodes the specified byte to this output stream. + * + * @param c the byte. + * @exception IOException if an I/O error occurs. + */ + @Override + public synchronized void write(int c) throws IOException { + buffer[bufsize++] = (byte)c; + if (bufsize == 3) { // Encoding unit = 3 bytes + encode(); + bufsize = 0; + } + } + + /** + * Flushes this output stream and forces any buffered output bytes + * to be encoded out to the stream. + * + * @exception IOException if an I/O error occurs. + */ + @Override + public synchronized void flush() throws IOException { + if (bufsize > 0) { // If there's unencoded characters in the buffer .. + encode(); // .. encode them + bufsize = 0; + } + out.flush(); + } + + /** + * Forces any buffered output bytes to be encoded out to the stream + * and closes this output stream + */ + @Override + public synchronized void close() throws IOException { + flush(); + if (count > 0 && !noCRLF) { + out.write(newline); + out.flush(); + } + out.close(); + } + + /** This array maps the characters to their 6 bit values */ + private final static char pem_array[] = { + 'A','B','C','D','E','F','G','H', // 0 + 'I','J','K','L','M','N','O','P', // 1 + 'Q','R','S','T','U','V','W','X', // 2 + 'Y','Z','a','b','c','d','e','f', // 3 + 'g','h','i','j','k','l','m','n', // 4 + 'o','p','q','r','s','t','u','v', // 5 + 'w','x','y','z','0','1','2','3', // 6 + '4','5','6','7','8','9','+','/' // 7 + }; + + /** + * Encode the data stored in buffer. + * Uses outbuf to store the encoded + * data before writing. + * + * @exception IOException if an I/O error occurs. + */ + private void encode() throws IOException { + int osize = encodedSize(bufsize); + out.write(encode(buffer, 0, bufsize, outbuf), 0, osize); + // increment count + count += osize; + // If writing out this encoded unit caused overflow, + // start a new line. + if (count >= bytesPerLine) { + if (!noCRLF) + out.write(newline); + count = 0; + } + } + + /** + * Base64 encode a byte array. No line breaks are inserted. + * This method is suitable for short strings, such as those + * in the IMAP AUTHENTICATE protocol, but not to encode the + * entire content of a MIME part. + * + * @param inbuf the byte array + * @return the encoded byte array + */ + public static byte[] encode(byte[] inbuf) { + if (inbuf.length == 0) + return inbuf; + return encode(inbuf, 0, inbuf.length, null); + } + + /** + * Internal use only version of encode. Allow specifying which + * part of the input buffer to encode. If outbuf is non-null, + * it's used as is. Otherwise, a new output buffer is allocated. + */ + private static byte[] encode(byte[] inbuf, int off, int size, + byte[] outbuf) { + if (outbuf == null) + outbuf = new byte[encodedSize(size)]; + int inpos, outpos; + int val; + for (inpos = off, outpos = 0; size >= 3; size -= 3, outpos += 4) { + val = inbuf[inpos++] & 0xff; + val <<= 8; + val |= inbuf[inpos++] & 0xff; + val <<= 8; + val |= inbuf[inpos++] & 0xff; + outbuf[outpos+3] = (byte)pem_array[val & 0x3f]; + val >>= 6; + outbuf[outpos+2] = (byte)pem_array[val & 0x3f]; + val >>= 6; + outbuf[outpos+1] = (byte)pem_array[val & 0x3f]; + val >>= 6; + outbuf[outpos+0] = (byte)pem_array[val & 0x3f]; + } + // done with groups of three, finish up any odd bytes left + if (size == 1) { + val = inbuf[inpos++] & 0xff; + val <<= 4; + outbuf[outpos+3] = (byte)'='; // pad character; + outbuf[outpos+2] = (byte)'='; // pad character; + outbuf[outpos+1] = (byte)pem_array[val & 0x3f]; + val >>= 6; + outbuf[outpos+0] = (byte)pem_array[val & 0x3f]; + } else if (size == 2) { + val = inbuf[inpos++] & 0xff; + val <<= 8; + val |= inbuf[inpos++] & 0xff; + val <<= 2; + outbuf[outpos+3] = (byte)'='; // pad character; + outbuf[outpos+2] = (byte)pem_array[val & 0x3f]; + val >>= 6; + outbuf[outpos+1] = (byte)pem_array[val & 0x3f]; + val >>= 6; + outbuf[outpos+0] = (byte)pem_array[val & 0x3f]; + } + return outbuf; + } + + /** + * Return the corresponding encoded size for the given number + * of bytes, not including any CRLF. + */ + private static int encodedSize(int size) { + return ((size + 2) / 3) * 4; + } + + /*** begin TEST program + public static void main(String argv[]) throws Exception { + FileInputStream infile = new FileInputStream(argv[0]); + BASE64EncoderStream encoder = new BASE64EncoderStream(System.out); + int c; + + while ((c = infile.read()) != -1) + encoder.write(c); + encoder.close(); + } + *** end TEST program **/ +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/BEncoderStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/BEncoderStream.java new file mode 100644 index 000000000..b6bf56540 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/BEncoderStream.java @@ -0,0 +1,74 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; + +/** + * This class implements a 'B' Encoder as defined by RFC2047 for + * encoding MIME headers. It subclasses the BASE64EncoderStream + * class. + * + * @author John Mani + */ + +public class BEncoderStream extends BASE64EncoderStream { + + /** + * Create a 'B' encoder that encodes the specified input stream. + * @param out the output stream + */ + public BEncoderStream(OutputStream out) { + super(out, Integer.MAX_VALUE); // MAX_VALUE is 2^31, should + // suffice (!) to indicate that + // CRLFs should not be inserted + } + + /** + * Returns the length of the encoded version of this byte array. + * + * @param b the byte array + * @return the length + */ + public static int encodedLength(byte[] b) { + return ((b.length + 2)/3) * 4; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/CRLFOutputStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/CRLFOutputStream.java new file mode 100644 index 000000000..4af34591e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/CRLFOutputStream.java @@ -0,0 +1,112 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; + + +/** + * Convert lines into the canonical format, that is, terminate lines with the + * CRLF sequence. + * + * @author John Mani + */ +public class CRLFOutputStream extends FilterOutputStream { + protected int lastb = -1; + protected boolean atBOL = true; // at beginning of line? + private static final byte[] newline = { (byte)'\r', (byte)'\n' }; + + public CRLFOutputStream(OutputStream os) { + super(os); + } + + @Override + public void write(int b) throws IOException { + if (b == '\r') { + writeln(); + } else if (b == '\n') { + if (lastb != '\r') + writeln(); + } else { + out.write(b); + atBOL = false; + } + lastb = b; + } + + @Override + public void write(byte b[]) throws IOException { + write(b, 0, b.length); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + int start = off; + + len += off; + for (int i = start; i < len ; i++) { + if (b[i] == '\r') { + out.write(b, start, i - start); + writeln(); + start = i + 1; + } else if (b[i] == '\n') { + if (lastb != '\r') { + out.write(b, start, i - start); + writeln(); + } + start = i + 1; + } + lastb = b[i]; + } + if ((len - start) > 0) { + out.write(b, start, len - start); + atBOL = false; + } + } + + /* + * Just write out a new line, something similar to out.println() + */ + public void writeln() throws IOException { + out.write(newline); + atBOL = true; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/DecodingException.java b/fine-third-default/fine-mail/src/com/sun/mail/util/DecodingException.java new file mode 100644 index 000000000..32bbc4270 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/DecodingException.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.IOException; + +/** + * A special IOException that indicates a failure to decode data due + * to an error in the formatting of the data. This allows applications + * to distinguish decoding errors from other I/O errors. + * + * @author Bill Shannon + */ + +public class DecodingException extends IOException { + + private static final long serialVersionUID = -6913647794421459390L; + + /** + * Constructor. + * + * @param s the exception message + */ + public DecodingException(String s) { + super(s); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/FolderClosedIOException.java b/fine-third-default/fine-mail/src/com/sun/mail/util/FolderClosedIOException.java new file mode 100644 index 000000000..e61a6944b --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/FolderClosedIOException.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.IOException; +import javax.mail.Folder; + +/** + * A variant of FolderClosedException that can be thrown from methods + * that only throw IOException. The getContent method will catch this + * exception and translate it back to FolderClosedException. + * + * @author Bill Shannon + */ + +public class FolderClosedIOException extends IOException { + transient private Folder folder; + + private static final long serialVersionUID = 4281122580365555735L; + + /** + * Constructor + * @param folder the Folder + */ + public FolderClosedIOException(Folder folder) { + this(folder, null); + } + + /** + * Constructor + * @param folder the Folder + * @param message the detailed error message + */ + public FolderClosedIOException(Folder folder, String message) { + super(message); + this.folder = folder; + } + + /** + * Returns the dead Folder object + * + * @return the dead Folder + */ + public Folder getFolder() { + return folder; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/LineInputStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/LineInputStream.java new file mode 100644 index 000000000..7cec94d25 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/LineInputStream.java @@ -0,0 +1,167 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +/** + * This class is to support reading CRLF terminated lines that + * contain only US-ASCII characters from an input stream. Provides + * functionality that is similar to the deprecated + * DataInputStream.readLine(). Expected use is to read + * lines as String objects from a RFC822 stream. + * + * It is implemented as a FilterInputStream, so one can just wrap + * this class around any input stream and read bytes from this filter. + * + * @author John Mani + * @author Bill Shannon + */ + +public class LineInputStream extends FilterInputStream { + + private boolean allowutf8; + private byte[] lineBuffer = null; // reusable byte buffer + private static int MAX_INCR = 1024*1024; // 1MB + + public LineInputStream(InputStream in) { + this(in, false); + } + + /** + * @param in the InputStream + * @param allowutf8 allow UTF-8 characters? + * @since JavaMail 1.6 + */ + public LineInputStream(InputStream in, boolean allowutf8) { + super(in); + this.allowutf8 = allowutf8; + } + + /** + * Read a line containing only ASCII characters from the input + * stream. A line is terminated by a CR or NL or CR-NL sequence. + * A common error is a CR-CR-NL sequence, which will also terminate + * a line. + * The line terminator is not returned as part of the returned + * String. Returns null if no data is available.

    + * + * This class is similar to the deprecated + * DataInputStream.readLine() + * + * @return the line + * @exception IOException for I/O errors + */ + @SuppressWarnings("deprecation") // for old String constructor + public String readLine() throws IOException { + //InputStream in = this.in; + byte[] buf = lineBuffer; + + if (buf == null) + buf = lineBuffer = new byte[128]; + + int c1; + int room = buf.length; + int offset = 0; + + while ((c1 = in.read()) != -1) { + if (c1 == '\n') // Got NL, outa here. + break; + else if (c1 == '\r') { + // Got CR, is the next char NL ? + boolean twoCRs = false; + if (in.markSupported()) + in.mark(2); + int c2 = in.read(); + if (c2 == '\r') { // discard extraneous CR + twoCRs = true; + c2 = in.read(); + } + if (c2 != '\n') { + /* + * If the stream supports it (which we hope will always + * be the case), reset to after the first CR. Otherwise, + * we wrap a PushbackInputStream around the stream so we + * can unread the characters we don't need. The only + * problem with that is that the caller might stop + * reading from this LineInputStream, throw it away, + * and then start reading from the underlying stream. + * If that happens, the pushed back characters will be + * lost forever. + */ + if (in.markSupported()) + in.reset(); + else { + if (!(in instanceof PushbackInputStream)) + in /*= this.in*/ = new PushbackInputStream(in, 2); + if (c2 != -1) + ((PushbackInputStream)in).unread(c2); + if (twoCRs) + ((PushbackInputStream)in).unread('\r'); + } + } + break; // outa here. + } + + // Not CR, NL or CR-NL ... + // .. Insert the byte into our byte buffer + if (--room < 0) { // No room, need to grow. + if (buf.length < MAX_INCR) + buf = new byte[buf.length * 2]; + else + buf = new byte[buf.length + MAX_INCR]; + room = buf.length - offset - 1; + System.arraycopy(lineBuffer, 0, buf, 0, offset); + lineBuffer = buf; + } + buf[offset++] = (byte)c1; + } + + if ((c1 == -1) && (offset == 0)) + return null; + + if (allowutf8) + return new String(buf, 0, offset, StandardCharsets.UTF_8); + else + return new String(buf, 0, 0, offset); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/LineOutputStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/LineOutputStream.java new file mode 100644 index 000000000..2e1479237 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/LineOutputStream.java @@ -0,0 +1,96 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +/** + * This class is to support writing out Strings as a sequence of bytes + * terminated by a CRLF sequence. The String must contain only US-ASCII + * characters.

    + * + * The expected use is to write out RFC822 style headers to an output + * stream.

    + * + * @author John Mani + * @author Bill Shannon + */ + +public class LineOutputStream extends FilterOutputStream { + private boolean allowutf8; + + private static byte[] newline; + + static { + newline = new byte[2]; + newline[0] = (byte)'\r'; + newline[1] = (byte)'\n'; + } + + public LineOutputStream(OutputStream out) { + this(out, false); + } + + /** + * @param out the OutputStream + * @param allowutf8 allow UTF-8 characters? + * @since JavaMail 1.6 + */ + public LineOutputStream(OutputStream out, boolean allowutf8) { + super(out); + this.allowutf8 = allowutf8; + } + + public void writeln(String s) throws IOException { + byte[] bytes; + if (allowutf8) + bytes = s.getBytes(StandardCharsets.UTF_8); + else + bytes = ASCIIUtility.getBytes(s); + out.write(bytes); + out.write(newline); + } + + public void writeln() throws IOException { + out.write(newline); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/LogOutputStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/LogOutputStream.java new file mode 100644 index 000000000..7c584843e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/LogOutputStream.java @@ -0,0 +1,152 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2008-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.logging.Level; + +/** + * Capture output lines and send them to the mail logger. + */ +public class LogOutputStream extends OutputStream { + protected MailLogger logger; + protected Level level; + + private int lastb = -1; + private byte[] buf = new byte[80]; + private int pos = 0; + + /** + * Log to the specified logger. + * + * @param logger the MailLogger + */ + public LogOutputStream(MailLogger logger) { + this.logger = logger; + this.level = Level.FINEST; + } + + @Override + public void write(int b) throws IOException { + if (!logger.isLoggable(level)) + return; + + if (b == '\r') { + logBuf(); + } else if (b == '\n') { + if (lastb != '\r') + logBuf(); + } else { + expandCapacity(1); + buf[pos++] = (byte)b; + } + lastb = b; + } + + @Override + public void write(byte b[]) throws IOException { + write(b, 0, b.length); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + int start = off; + + if (!logger.isLoggable(level)) + return; + len += off; + for (int i = start; i < len ; i++) { + if (b[i] == '\r') { + expandCapacity(i - start); + System.arraycopy(b, start, buf, pos, i - start); + pos += i - start; + logBuf(); + start = i + 1; + } else if (b[i] == '\n') { + if (lastb != '\r') { + expandCapacity(i - start); + System.arraycopy(b, start, buf, pos, i - start); + pos += i - start; + logBuf(); + } + start = i + 1; + } + lastb = b[i]; + } + if ((len - start) > 0) { + expandCapacity(len - start); + System.arraycopy(b, start, buf, pos, len - start); + pos += len - start; + } + } + + /** + * Log the specified message. + * Can be overridden by subclass to do different logging. + * + * @param msg the message to log + */ + protected void log(String msg) { + logger.log(level, msg); + } + + /** + * Convert the buffer to a string and log it. + */ + private void logBuf() { + String msg = new String(buf, 0, pos); + pos = 0; + log(msg); + } + + /** + * Ensure that the buffer can hold at least len bytes + * beyond the current position. + */ + private void expandCapacity(int len) { + while (pos + len > buf.length) { + byte[] nb = new byte[buf.length * 2]; + System.arraycopy(buf, 0, nb, 0, pos); + buf = nb; + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/MailConnectException.java b/fine-third-default/fine-mail/src/com/sun/mail/util/MailConnectException.java new file mode 100644 index 000000000..1c5c96a51 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/MailConnectException.java @@ -0,0 +1,107 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import javax.mail.MessagingException; + +/** + * A MessagingException that indicates a socket connection attempt failed. + * Unlike java.net.ConnectException, it includes details of what we + * were trying to connect to. The underlying exception is available + * as the "cause" of this exception. + * + * @see java.net.ConnectException + * @author Bill Shannon + * @since JavaMail 1.5.0 + */ + +public class MailConnectException extends MessagingException { + private String host; + private int port; + private int cto; + + private static final long serialVersionUID = -3818807731125317729L; + + /** + * Constructs a MailConnectException. + * + * @param cex the SocketConnectException with the details + */ + public MailConnectException(SocketConnectException cex) { + super( + "Couldn't connect to host, port: " + + cex.getHost() + ", " + cex.getPort() + + "; timeout " + cex.getConnectionTimeout() + + (cex.getMessage() != null ? ("; " + cex.getMessage()) : "")); + // extract the details and save them here + this.host = cex.getHost(); + this.port = cex.getPort(); + this.cto = cex.getConnectionTimeout(); + setNextException(cex.getException()); + } + + /** + * The host we were trying to connect to. + * + * @return the host + */ + public String getHost() { + return host; + } + + /** + * The port we were trying to connect to. + * + * @return the port + */ + public int getPort() { + return port; + } + + /** + * The timeout used for the connection attempt. + * + * @return the connection timeout + */ + public int getConnectionTimeout() { + return cto; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/MailLogger.java b/fine-third-default/fine-mail/src/com/sun/mail/util/MailLogger.java new file mode 100644 index 000000000..f7d9fab92 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/MailLogger.java @@ -0,0 +1,443 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.PrintStream; +import java.text.MessageFormat; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.mail.Session; + +/** + * A simplified logger used by JavaMail to handle logging to a + * PrintStream and logging through a java.util.logging.Logger. + * If debug is set, messages are written to the PrintStream and + * prefixed by the specified prefix (which is not included in + * Logger messages). + * Messages are logged by the Logger based on the configuration + * of the logging system. + */ + +/* + * It would be so much simpler to just subclass Logger and override + * the log(LogRecord) method, as the javadocs suggest, but that doesn't + * work because Logger makes the decision about whether to log the message + * or not before calling the log(LogRecord) method. Instead, we just + * provide the few log methods we need here. + */ + +public final class MailLogger { + /** + * For log messages. + */ + private final Logger logger; + /** + * For debug output. + */ + private final String prefix; + /** + * Produce debug output? + */ + private final boolean debug; + /** + * Stream for debug output. + */ + private final PrintStream out; + + /** + * Construct a new MailLogger using the specified Logger name, + * debug prefix (e.g., "DEBUG"), debug flag, and PrintStream. + * + * @param name the Logger name + * @param prefix the prefix for debug output, or null for none + * @param debug if true, write to PrintStream + * @param out the PrintStream to write to + */ + public MailLogger(String name, String prefix, boolean debug, + PrintStream out) { + logger = Logger.getLogger(name); + this.prefix = prefix; + this.debug = debug; + this.out = out != null ? out : System.out; + } + + /** + * Construct a new MailLogger using the specified class' package + * name as the Logger name, + * debug prefix (e.g., "DEBUG"), debug flag, and PrintStream. + * + * @param clazz the Logger name is the package name of this class + * @param prefix the prefix for debug output, or null for none + * @param debug if true, write to PrintStream + * @param out the PrintStream to write to + */ + public MailLogger(Class clazz, String prefix, boolean debug, + PrintStream out) { + String name = packageOf(clazz); + logger = Logger.getLogger(name); + this.prefix = prefix; + this.debug = debug; + this.out = out != null ? out : System.out; + } + + /** + * Construct a new MailLogger using the specified class' package + * name combined with the specified subname as the Logger name, + * debug prefix (e.g., "DEBUG"), debug flag, and PrintStream. + * + * @param clazz the Logger name is the package name of this class + * @param subname the Logger name relative to this Logger name + * @param prefix the prefix for debug output, or null for none + * @param debug if true, write to PrintStream + * @param out the PrintStream to write to + */ + public MailLogger(Class clazz, String subname, String prefix, boolean debug, + PrintStream out) { + String name = packageOf(clazz) + "." + subname; + logger = Logger.getLogger(name); + this.prefix = prefix; + this.debug = debug; + this.out = out != null ? out : System.out; + } + + /** + * Construct a new MailLogger using the specified Logger name and + * debug prefix (e.g., "DEBUG"). Get the debug flag and PrintStream + * from the Session. + * + * @param name the Logger name + * @param prefix the prefix for debug output, or null for none + * @param session where to get the debug flag and PrintStream + */ + @Deprecated + public MailLogger(String name, String prefix, Session session) { + this(name, prefix, session.getDebug(), session.getDebugOut()); + } + + /** + * Construct a new MailLogger using the specified class' package + * name as the Logger name and the specified + * debug prefix (e.g., "DEBUG"). Get the debug flag and PrintStream + * from the Session. + * + * @param clazz the Logger name is the package name of this class + * @param prefix the prefix for debug output, or null for none + * @param session where to get the debug flag and PrintStream + */ + @Deprecated + public MailLogger(Class clazz, String prefix, Session session) { + this(clazz, prefix, session.getDebug(), session.getDebugOut()); + } + + /** + * Create a MailLogger that uses a Logger with the specified name + * and prefix. The new MailLogger uses the same debug flag and + * PrintStream as this MailLogger. + * + * @param name the Logger name + * @param prefix the prefix for debug output, or null for none + * @return a MailLogger for the given name and prefix. + */ + public MailLogger getLogger(String name, String prefix) { + return new MailLogger(name, prefix, debug, out); + } + + /** + * Create a MailLogger using the specified class' package + * name as the Logger name and the specified prefix. + * The new MailLogger uses the same debug flag and + * PrintStream as this MailLogger. + * + * @param clazz the Logger name is the package name of this class + * @param prefix the prefix for debug output, or null for none + * @return a MailLogger for the given name and prefix. + */ + public MailLogger getLogger(Class clazz, String prefix) { + return new MailLogger(clazz, prefix, debug, out); + } + + /** + * Create a MailLogger that uses a Logger whose name is composed + * of this MailLogger's name plus the specified sub-name, separated + * by a dot. The new MailLogger uses the new prefix for debug output. + * This is used primarily by the protocol trace code that wants a + * different prefix (none). + * + * @param subname the Logger name relative to this Logger name + * @param prefix the prefix for debug output, or null for none + * @return a MailLogger for the given name and prefix. + */ + public MailLogger getSubLogger(String subname, String prefix) { + return new MailLogger(logger.getName() + "." + subname, prefix, + debug, out); + } + + /** + * Create a MailLogger that uses a Logger whose name is composed + * of this MailLogger's name plus the specified sub-name, separated + * by a dot. The new MailLogger uses the new prefix for debug output. + * This is used primarily by the protocol trace code that wants a + * different prefix (none). + * + * @param subname the Logger name relative to this Logger name + * @param prefix the prefix for debug output, or null for none + * @param debug the debug flag for the sub-logger + * @return a MailLogger for the given name and prefix. + */ + public MailLogger getSubLogger(String subname, String prefix, + boolean debug) { + return new MailLogger(logger.getName() + "." + subname, prefix, + debug, out); + } + + /** + * Log the message at the specified level. + * @param level the log level. + * @param msg the message. + */ + public void log(Level level, String msg) { + ifDebugOut(msg); + if (logger.isLoggable(level)) { + final StackTraceElement frame = inferCaller(); + logger.logp(level, frame.getClassName(), frame.getMethodName(), msg); + } + } + + /** + * Log the message at the specified level. + * @param level the log level. + * @param msg the message. + * @param param1 the additional parameter. + */ + public void log(Level level, String msg, Object param1) { + if (debug) { + msg = MessageFormat.format(msg, new Object[] { param1 }); + debugOut(msg); + } + + if (logger.isLoggable(level)) { + final StackTraceElement frame = inferCaller(); + logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, param1); + } + } + + /** + * Log the message at the specified level. + * @param level the log level. + * @param msg the message. + * @param params the message parameters. + */ + public void log(Level level, String msg, Object... params) { + if (debug) { + msg = MessageFormat.format(msg, params); + debugOut(msg); + } + + if (logger.isLoggable(level)) { + final StackTraceElement frame = inferCaller(); + logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, params); + } + } + + /** + * Log the message at the specified level using a format string. + * @param level the log level. + * @param msg the message format string. + * @param params the message parameters. + * + * @since JavaMail 1.5.4 + */ + public void logf(Level level, String msg, Object... params) { + msg = String.format(msg, params); + ifDebugOut(msg); + logger.log(level, msg); + } + + /** + * Log the message at the specified level. + * @param level the log level. + * @param msg the message. + * @param thrown the throwable to log. + */ + public void log(Level level, String msg, Throwable thrown) { + if (debug) { + if (thrown != null) { + debugOut(msg + ", THROW: "); + thrown.printStackTrace(out); + } else { + debugOut(msg); + } + } + + if (logger.isLoggable(level)) { + final StackTraceElement frame = inferCaller(); + logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, thrown); + } + } + + /** + * Log a message at the CONFIG level. + * @param msg the message. + */ + public void config(String msg) { + log(Level.CONFIG, msg); + } + + /** + * Log a message at the FINE level. + * @param msg the message. + */ + public void fine(String msg) { + log(Level.FINE, msg); + } + + /** + * Log a message at the FINER level. + * @param msg the message. + */ + public void finer(String msg) { + log(Level.FINER, msg); + } + + /** + * Log a message at the FINEST level. + * @param msg the message. + */ + public void finest(String msg) { + log(Level.FINEST, msg); + } + + /** + * If "debug" is set, or our embedded Logger is loggable at the + * given level, return true. + * @param level the log level. + * @return true if loggable. + */ + public boolean isLoggable(Level level) { + return debug || logger.isLoggable(level); + } + + /** + * Common code to conditionally log debug statements. + * @param msg the message to log. + */ + private void ifDebugOut(String msg) { + if (debug) + debugOut(msg); + } + + /** + * Common formatting for debug output. + * @param msg the message to log. + */ + private void debugOut(String msg) { + if (prefix != null) + out.println(prefix + ": " + msg); + else + out.println(msg); + } + + /** + * Return the package name of the class. + * Sometimes there will be no Package object for the class, + * e.g., if the class loader hasn't created one (see Class.getPackage()). + * @param clazz the class source. + * @return the package name or an empty string. + */ + private String packageOf(Class clazz) { + Package p = clazz.getPackage(); + if (p != null) + return p.getName(); // hopefully the common case + String cname = clazz.getName(); + int i = cname.lastIndexOf('.'); + if (i > 0) + return cname.substring(0, i); + // no package name, now what? + return ""; + } + + /** + * A disadvantage of not being able to use Logger directly in JavaMail + * code is that the "source class" information that Logger guesses will + * always refer to this class instead of our caller. This method + * duplicates what Logger does to try to find *our* caller, so that + * Logger doesn't have to do it (and get the wrong answer), and because + * our caller is what's wanted. + * @return StackTraceElement that logged the message. Treat as read-only. + */ + private StackTraceElement inferCaller() { + // Get the stack trace. + StackTraceElement stack[] = (new Throwable()).getStackTrace(); + // First, search back to a method in the Logger class. + int ix = 0; + while (ix < stack.length) { + StackTraceElement frame = stack[ix]; + String cname = frame.getClassName(); + if (isLoggerImplFrame(cname)) { + break; + } + ix++; + } + // Now search for the first frame before the "Logger" class. + while (ix < stack.length) { + StackTraceElement frame = stack[ix]; + String cname = frame.getClassName(); + if (!isLoggerImplFrame(cname)) { + // We've found the relevant frame. + return frame; + } + ix++; + } + // We haven't found a suitable frame, so just punt. This is + // OK as we are only committed to making a "best effort" here. + return new StackTraceElement(MailLogger.class.getName(), "log", + MailLogger.class.getName(), -1); + } + + /** + * Frames to ignore as part of the MailLogger to JUL bridge. + * @param cname the class name. + * @return true if the class name is part of the MailLogger bridge. + */ + private boolean isLoggerImplFrame(String cname) { + return MailLogger.class.getName().equals(cname); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/MailSSLSocketFactory.java b/fine-third-default/fine-mail/src/com/sun/mail/util/MailSSLSocketFactory.java new file mode 100644 index 000000000..dfb2081e9 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/MailSSLSocketFactory.java @@ -0,0 +1,380 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; +import java.net.*; +import java.security.*; +import java.security.cert.*; +import java.util.*; + +import javax.net.ssl.*; + +/** + * An SSL socket factory that makes it easier to specify trust. + * This socket factory can be configured to trust all hosts or + * trust a specific set of hosts, in which case the server's + * certificate isn't verified. Alternatively, a custom TrustManager + * can be supplied.

    + * + * An instance of this factory can be set as the value of the + * mail.<protocol>.ssl.socketFactory property. + * + * @since JavaMail 1.4.2 + * @author Stephan Sann + * @author Bill Shannon + */ +public class MailSSLSocketFactory extends SSLSocketFactory { + + /** Should all hosts be trusted? */ + private boolean trustAllHosts; + + /** String-array of trusted hosts */ + private String[] trustedHosts = null; + + /** Holds a SSLContext to get SSLSocketFactories from */ + private SSLContext sslcontext; + + /** Holds the KeyManager array to use */ + private KeyManager[] keyManagers; + + /** Holds the TrustManager array to use */ + private TrustManager[] trustManagers; + + /** Holds the SecureRandom to use */ + private SecureRandom secureRandom; + + /** Holds a SSLSocketFactory to pass all API-method-calls to */ + private SSLSocketFactory adapteeFactory = null; + + /** + * Initializes a new MailSSLSocketFactory. + * + * @throws GeneralSecurityException for security errors + */ + public MailSSLSocketFactory() throws GeneralSecurityException { + this("TLS"); + } + + /** + * Initializes a new MailSSLSocketFactory with a given protocol. + * Normally the protocol will be specified as "TLS". + * + * @param protocol The protocol to use + * @throws NoSuchAlgorithmException if given protocol is not supported + * @throws GeneralSecurityException for security errors + */ + public MailSSLSocketFactory(String protocol) + throws GeneralSecurityException { + + // By default we do NOT trust all hosts. + trustAllHosts = false; + + // Get an instance of an SSLContext. + sslcontext = SSLContext.getInstance(protocol); + + // Default properties to init the SSLContext + keyManagers = null; + trustManagers = new TrustManager[] { new MailTrustManager() }; + secureRandom = null; + + // Assemble a default SSLSocketFactory to delegate all API-calls to. + newAdapteeFactory(); + } + + + /** + * Gets an SSLSocketFactory based on the given (or default) + * KeyManager array, TrustManager array and SecureRandom and + * sets it to the instance var adapteeFactory. + * + * @throws KeyManagementException for key manager errors + */ + private synchronized void newAdapteeFactory() + throws KeyManagementException { + sslcontext.init(keyManagers, trustManagers, secureRandom); + + // Get SocketFactory and save it in our instance var + adapteeFactory = sslcontext.getSocketFactory(); + } + + /** + * @return the keyManagers + */ + public synchronized KeyManager[] getKeyManagers() { + return keyManagers.clone(); + } + + /** + * @param keyManagers the keyManagers to set + * @throws GeneralSecurityException for security errors + */ + public synchronized void setKeyManagers(KeyManager[] keyManagers) + throws GeneralSecurityException { + this.keyManagers = keyManagers.clone(); + newAdapteeFactory(); + } + + /** + * @return the secureRandom + */ + public synchronized SecureRandom getSecureRandom() { + return secureRandom; + } + + /** + * @param secureRandom the secureRandom to set + * @throws GeneralSecurityException for security errors + */ + public synchronized void setSecureRandom(SecureRandom secureRandom) + throws GeneralSecurityException { + this.secureRandom = secureRandom; + newAdapteeFactory(); + } + + /** + * @return the trustManagers + */ + public synchronized TrustManager[] getTrustManagers() { + return trustManagers; + } + + /** + * @param trustManagers the trustManagers to set + * @throws GeneralSecurityException for security errors + */ + public synchronized void setTrustManagers(TrustManager[] trustManagers) + throws GeneralSecurityException { + this.trustManagers = trustManagers; + newAdapteeFactory(); + } + + /** + * @return true if all hosts should be trusted + */ + public synchronized boolean isTrustAllHosts() { + return trustAllHosts; + } + + /** + * @param trustAllHosts should all hosts be trusted? + */ + public synchronized void setTrustAllHosts(boolean trustAllHosts) { + this.trustAllHosts = trustAllHosts; + } + + /** + * @return the trusted hosts + */ + public synchronized String[] getTrustedHosts() { + if (trustedHosts == null) + return null; + else + return trustedHosts.clone(); + } + + /** + * @param trustedHosts the hosts to trust + */ + public synchronized void setTrustedHosts(String[] trustedHosts) { + if (trustedHosts == null) + this.trustedHosts = null; + else + this.trustedHosts = trustedHosts.clone(); + } + + /** + * After a successful conection to the server, this method is + * called to ensure that the server should be trusted. + * + * @param server name of the server we connected to + * @param sslSocket SSLSocket connected to the server + * @return true if "trustAllHosts" is set to true OR the server + * is contained in the "trustedHosts" array; + */ + public synchronized boolean isServerTrusted(String server, + SSLSocket sslSocket) { + + //System.out.println("DEBUG: isServerTrusted host " + server); + + // If "trustAllHosts" is set to true, we return true + if (trustAllHosts) + return true; + + // If the socket host is contained in the "trustedHosts" array, + // we return true + if (trustedHosts != null) + return Arrays.asList(trustedHosts).contains(server); // ignore case? + + // If we get here, trust of the server was verified by the trust manager + return true; + } + + + // SocketFactory methods + + /* (non-Javadoc) + * @see javax.net.ssl.SSLSocketFactory#createSocket(java.net.Socket, + * java.lang.String, int, boolean) + */ + @Override + public synchronized Socket createSocket(Socket socket, String s, int i, + boolean flag) throws IOException { + return adapteeFactory.createSocket(socket, s, i, flag); + } + + /* (non-Javadoc) + * @see javax.net.ssl.SSLSocketFactory#getDefaultCipherSuites() + */ + @Override + public synchronized String[] getDefaultCipherSuites() { + return adapteeFactory.getDefaultCipherSuites(); + } + + /* (non-Javadoc) + * @see javax.net.ssl.SSLSocketFactory#getSupportedCipherSuites() + */ + @Override + public synchronized String[] getSupportedCipherSuites() { + return adapteeFactory.getSupportedCipherSuites(); + } + + /* (non-Javadoc) + * @see javax.net.SocketFactory#createSocket() + */ + @Override + public synchronized Socket createSocket() throws IOException { + return adapteeFactory.createSocket(); + } + + /* (non-Javadoc) + * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int, + * java.net.InetAddress, int) + */ + @Override + public synchronized Socket createSocket(InetAddress inetaddress, int i, + InetAddress inetaddress1, int j) throws IOException { + return adapteeFactory.createSocket(inetaddress, i, inetaddress1, j); + } + + /* (non-Javadoc) + * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int) + */ + @Override + public synchronized Socket createSocket(InetAddress inetaddress, int i) + throws IOException { + return adapteeFactory.createSocket(inetaddress, i); + } + + /* (non-Javadoc) + * @see javax.net.SocketFactory#createSocket(java.lang.String, int, + * java.net.InetAddress, int) + */ + @Override + public synchronized Socket createSocket(String s, int i, + InetAddress inetaddress, int j) + throws IOException, UnknownHostException { + return adapteeFactory.createSocket(s, i, inetaddress, j); + } + + /* (non-Javadoc) + * @see javax.net.SocketFactory#createSocket(java.lang.String, int) + */ + @Override + public synchronized Socket createSocket(String s, int i) + throws IOException, UnknownHostException { + return adapteeFactory.createSocket(s, i); + } + + + // inner classes + + /** + * A default Trustmanager. + * + * @author Stephan Sann + */ + private class MailTrustManager implements X509TrustManager { + + /** A TrustManager to pass method calls to */ + private X509TrustManager adapteeTrustManager = null; + + /** + * Initializes a new TrustManager instance. + */ + private MailTrustManager() throws GeneralSecurityException { + TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); + tmf.init((KeyStore)null); + adapteeTrustManager = (X509TrustManager)tmf.getTrustManagers()[0]; + } + + /* (non-Javadoc) + * @see javax.net.ssl.X509TrustManager#checkClientTrusted( + * java.security.cert.X509Certificate[], java.lang.String) + */ + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) + throws CertificateException { + if (!(isTrustAllHosts() || getTrustedHosts() != null)) + adapteeTrustManager.checkClientTrusted(certs, authType); + } + + /* (non-Javadoc) + * @see javax.net.ssl.X509TrustManager#checkServerTrusted( + * java.security.cert.X509Certificate[], java.lang.String) + */ + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) + throws CertificateException { + + if (!(isTrustAllHosts() || getTrustedHosts() != null)) + adapteeTrustManager.checkServerTrusted(certs, authType); + } + + /* (non-Javadoc) + * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() + */ + @Override + public X509Certificate[] getAcceptedIssuers() { + return adapteeTrustManager.getAcceptedIssuers(); + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/MessageRemovedIOException.java b/fine-third-default/fine-mail/src/com/sun/mail/util/MessageRemovedIOException.java new file mode 100644 index 000000000..a4e9e8141 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/MessageRemovedIOException.java @@ -0,0 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.IOException; + +/** + * A variant of MessageRemovedException that can be thrown from methods + * that only throw IOException. The getContent method will catch this + * exception and translate it back to MessageRemovedException. + * + * @see javax.mail.Message#isExpunged() + * @see javax.mail.Message#getMessageNumber() + * @author Bill Shannon + */ + +public class MessageRemovedIOException extends IOException { + + private static final long serialVersionUID = 4280468026581616424L; + + /** + * Constructs a MessageRemovedIOException with no detail message. + */ + public MessageRemovedIOException() { + super(); + } + + /** + * Constructs a MessageRemovedIOException with the specified detail message. + * @param s the detail message + */ + public MessageRemovedIOException(String s) { + super(s); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/MimeUtil.java b/fine-third-default/fine-mail/src/com/sun/mail/util/MimeUtil.java new file mode 100644 index 000000000..3f0d2d27a --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/MimeUtil.java @@ -0,0 +1,128 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.lang.reflect.*; +import java.security.*; + +import javax.mail.internet.MimePart; + +/** + * General MIME-related utility methods. + * + * @author Bill Shannon + * @since JavaMail 1.4.4 + */ +public class MimeUtil { + + private static final Method cleanContentType; + + static { + Method meth = null; + try { + String cth = System.getProperty("mail.mime.contenttypehandler"); + if (cth != null) { + ClassLoader cl = getContextClassLoader(); + Class clsHandler = null; + if (cl != null) { + try { + clsHandler = Class.forName(cth, false, cl); + } catch (ClassNotFoundException cex) { } + } + if (clsHandler == null) + clsHandler = Class.forName(cth); + meth = clsHandler.getMethod("cleanContentType", + new Class[] { MimePart.class, String.class }); + } + } catch (ClassNotFoundException ex) { + // ignore it + } catch (NoSuchMethodException ex) { + // ignore it + } catch (RuntimeException ex) { + // ignore it + } finally { + cleanContentType = meth; + } + } + + // No one should instantiate this class. + private MimeUtil() { + } + + /** + * If a Content-Type handler has been specified, + * call it to clean up the Content-Type value. + * + * @param mp the MimePart + * @param contentType the Content-Type value + * @return the cleaned Content-Type value + */ + public static String cleanContentType(MimePart mp, String contentType) { + if (cleanContentType != null) { + try { + return (String)cleanContentType.invoke(null, + new Object[] { mp, contentType }); + } catch (Exception ex) { + return contentType; + } + } else + return contentType; + } + + /** + * Convenience method to get our context class loader. + * Assert any privileges we might have and then call the + * Thread.getContextClassLoader method. + */ + private static ClassLoader getContextClassLoader() { + return + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public ClassLoader run() { + ClassLoader cl = null; + try { + cl = Thread.currentThread().getContextClassLoader(); + } catch (SecurityException ex) { } + return cl; + } + }); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/PropUtil.java b/fine-third-default/fine-mail/src/com/sun/mail/util/PropUtil.java new file mode 100644 index 000000000..87ce85ae3 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/PropUtil.java @@ -0,0 +1,193 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.util.*; +import javax.mail.Session; + +/** + * Utilities to make it easier to get property values. + * Properties can be strings or type-specific value objects. + * + * @author Bill Shannon + */ +public class PropUtil { + + // No one should instantiate this class. + private PropUtil() { + } + + /** + * Get an integer valued property. + * + * @param props the properties + * @param name the property name + * @param def default value if property not found + * @return the property value + */ + public static int getIntProperty(Properties props, String name, int def) { + return getInt(getProp(props, name), def); + } + + /** + * Get a boolean valued property. + * + * @param props the properties + * @param name the property name + * @param def default value if property not found + * @return the property value + */ + public static boolean getBooleanProperty(Properties props, + String name, boolean def) { + return getBoolean(getProp(props, name), def); + } + + /** + * Get an integer valued property. + * + * @param session the Session + * @param name the property name + * @param def default value if property not found + * @return the property value + */ + @Deprecated + public static int getIntSessionProperty(Session session, + String name, int def) { + return getInt(getProp(session.getProperties(), name), def); + } + + /** + * Get a boolean valued property. + * + * @param session the Session + * @param name the property name + * @param def default value if property not found + * @return the property value + */ + @Deprecated + public static boolean getBooleanSessionProperty(Session session, + String name, boolean def) { + return getBoolean(getProp(session.getProperties(), name), def); + } + + /** + * Get a boolean valued System property. + * + * @param name the property name + * @param def default value if property not found + * @return the property value + */ + public static boolean getBooleanSystemProperty(String name, boolean def) { + try { + return getBoolean(getProp(System.getProperties(), name), def); + } catch (SecurityException sex) { + // fall through... + } + + /* + * If we can't get the entire System Properties object because + * of a SecurityException, just ask for the specific property. + */ + try { + String value = System.getProperty(name); + if (value == null) + return def; + if (def) + return !value.equalsIgnoreCase("false"); + else + return value.equalsIgnoreCase("true"); + } catch (SecurityException sex) { + return def; + } + } + + /** + * Get the value of the specified property. + * If the "get" method returns null, use the getProperty method, + * which might cascade to a default Properties object. + */ + private static Object getProp(Properties props, String name) { + Object val = props.get(name); + if (val != null) + return val; + else + return props.getProperty(name); + } + + /** + * Interpret the value object as an integer, + * returning def if unable. + */ + private static int getInt(Object value, int def) { + if (value == null) + return def; + if (value instanceof String) { + try { + return Integer.parseInt((String)value); + } catch (NumberFormatException nfex) { } + } + if (value instanceof Integer) + return ((Integer)value).intValue(); + return def; + } + + /** + * Interpret the value object as a boolean, + * returning def if unable. + */ + private static boolean getBoolean(Object value, boolean def) { + if (value == null) + return def; + if (value instanceof String) { + /* + * If the default is true, only "false" turns it off. + * If the default is false, only "true" turns it on. + */ + if (def) + return !((String)value).equalsIgnoreCase("false"); + else + return ((String)value).equalsIgnoreCase("true"); + } + if (value instanceof Boolean) + return ((Boolean)value).booleanValue(); + return def; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/QDecoderStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/QDecoderStream.java new file mode 100644 index 000000000..6ea8a0f05 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/QDecoderStream.java @@ -0,0 +1,94 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; + +/** + * This class implements a Q Decoder as defined in RFC 2047 + * for decoding MIME headers. It subclasses the QPDecoderStream class. + * + * @author John Mani + */ + +public class QDecoderStream extends QPDecoderStream { + + /** + * Create a Q-decoder that decodes the specified input stream. + * @param in the input stream + */ + public QDecoderStream(InputStream in) { + super(in); + } + + /** + * Read the next decoded byte from this input stream. The byte + * is returned as an int in the range 0 + * to 255. If no byte is available because the end of + * the stream has been reached, the value -1 is returned. + * This method blocks until input data is available, the end of the + * stream is detected, or an exception is thrown. + * + * @return the next byte of data, or -1 if the end of the + * stream is reached. + * @exception IOException if an I/O error occurs. + */ + @Override + public int read() throws IOException { + int c = in.read(); + + if (c == '_') // Return '_' as ' ' + return ' '; + else if (c == '=') { + // QP Encoded atom. Get the next two bytes .. + ba[0] = (byte)in.read(); + ba[1] = (byte)in.read(); + // .. and decode them + try { + return ASCIIUtility.parseInt(ba, 0, 2, 16); + } catch (NumberFormatException nex) { + throw new DecodingException( + "QDecoder: Error in QP stream " + nex.getMessage()); + } + } else + return c; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/QEncoderStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/QEncoderStream.java new file mode 100644 index 000000000..cb9d31d9d --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/QEncoderStream.java @@ -0,0 +1,124 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; + +/** + * This class implements a Q Encoder as defined by RFC 2047 for + * encoding MIME headers. It subclasses the QPEncoderStream class. + * + * @author John Mani + */ + +public class QEncoderStream extends QPEncoderStream { + + private String specials; + private static String WORD_SPECIALS = "=_?\"#$%&'(),.:;<>@[\\]^`{|}~"; + private static String TEXT_SPECIALS = "=_?"; + + /** + * Create a Q encoder that encodes the specified input stream + * @param out the output stream + * @param encodingWord true if we are Q-encoding a word within a + * phrase. + */ + public QEncoderStream(OutputStream out, boolean encodingWord) { + super(out, Integer.MAX_VALUE); // MAX_VALUE is 2^31, should + // suffice (!) to indicate that + // CRLFs should not be inserted + // when encoding rfc822 headers + + // a RFC822 "word" token has more restrictions than a + // RFC822 "text" token. + specials = encodingWord ? WORD_SPECIALS : TEXT_SPECIALS; + } + + /** + * Encodes the specified byte to this output stream. + * @param c the byte. + * @exception IOException if an I/O error occurs. + */ + @Override + public void write(int c) throws IOException { + c = c & 0xff; // Turn off the MSB. + if (c == ' ') + output('_', false); + else if (c < 040 || c >= 0177 || specials.indexOf(c) >= 0) + // Encoding required. + output(c, true); + else // No encoding required + output(c, false); + } + + /** + * Returns the length of the encoded version of this byte array. + * + * @param b the byte array + * @param encodingWord true if encoding words, false if encoding text + * @return the length + */ + public static int encodedLength(byte[] b, boolean encodingWord) { + int len = 0; + String specials = encodingWord ? WORD_SPECIALS: TEXT_SPECIALS; + for (int i = 0; i < b.length; i++) { + int c = b[i] & 0xff; // Mask off MSB + if (c < 040 || c >= 0177 || specials.indexOf(c) >= 0) + // needs encoding + len += 3; // Q-encoding is 1 -> 3 conversion + else + len++; + } + return len; + } + + /**** begin TEST program *** + public static void main(String argv[]) throws Exception { + FileInputStream infile = new FileInputStream(argv[0]); + QEncoderStream encoder = new QEncoderStream(System.out); + int c; + + while ((c = infile.read()) != -1) + encoder.write(c); + encoder.close(); + } + *** end TEST program ***/ +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/QPDecoderStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/QPDecoderStream.java new file mode 100644 index 000000000..20cd14025 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/QPDecoderStream.java @@ -0,0 +1,222 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; + +/** + * This class implements a QP Decoder. It is implemented as + * a FilterInputStream, so one can just wrap this class around + * any input stream and read bytes from this filter. The decoding + * is done as the bytes are read out. + * + * @author John Mani + */ + +public class QPDecoderStream extends FilterInputStream { + protected byte[] ba = new byte[2]; + protected int spaces = 0; + + /** + * Create a Quoted Printable decoder that decodes the specified + * input stream. + * @param in the input stream + */ + public QPDecoderStream(InputStream in) { + super(new PushbackInputStream(in, 2)); // pushback of size=2 + } + + /** + * Read the next decoded byte from this input stream. The byte + * is returned as an int in the range 0 + * to 255. If no byte is available because the end of + * the stream has been reached, the value -1 is returned. + * This method blocks until input data is available, the end of the + * stream is detected, or an exception is thrown. + * + * @return the next byte of data, or -1 if the end of the + * stream is reached. + * @exception IOException if an I/O error occurs. + */ + @Override + public int read() throws IOException { + if (spaces > 0) { + // We have cached space characters, return one + spaces--; + return ' '; + } + + int c = in.read(); + + if (c == ' ') { + // Got space, keep reading till we get a non-space char + while ((c = in.read()) == ' ') + spaces++; + + if (c == '\r' || c == '\n' || c == -1) + // If the non-space char is CR/LF/EOF, the spaces we got + // so far is junk introduced during transport. Junk 'em. + spaces = 0; + else { + // The non-space char is NOT CR/LF, the spaces are valid. + ((PushbackInputStream)in).unread(c); + c = ' '; + } + return c; // return either or + } + else if (c == '=') { + // QP Encoded atom. Decode the next two bytes + int a = in.read(); + + if (a == '\n') { + /* Hmm ... not really confirming QP encoding, but lets + * allow this as a LF terminated encoded line .. and + * consider this a soft linebreak and recurse to fetch + * the next char. + */ + return read(); + } else if (a == '\r') { + // Expecting LF. This forms a soft linebreak to be ignored. + int b = in.read(); + if (b != '\n') + /* Not really confirming QP encoding, but + * lets allow this as well. + */ + ((PushbackInputStream)in).unread(b); + return read(); + } else if (a == -1) { + // Not valid QP encoding, but we be nice and tolerant here ! + return -1; + } else { + ba[0] = (byte)a; + ba[1] = (byte)in.read(); + try { + return ASCIIUtility.parseInt(ba, 0, 2, 16); + } catch (NumberFormatException nex) { + /* + System.err.println( + "Illegal characters in QP encoded stream: " + + ASCIIUtility.toString(ba, 0, 2) + ); + */ + + ((PushbackInputStream)in).unread(ba); + return c; + } + } + } + return c; + } + + /** + * Reads up to len decoded bytes of data from this input stream + * into an array of bytes. This method blocks until some input is + * available. + *

    + * + * @param buf the buffer into which the data is read. + * @param off the start offset of the data. + * @param len the maximum number of bytes read. + * @return the total number of bytes read into the buffer, or + * -1 if there is no more data because the end of + * the stream has been reached. + * @exception IOException if an I/O error occurs. + */ + @Override + public int read(byte[] buf, int off, int len) throws IOException { + int i, c; + for (i = 0; i < len; i++) { + if ((c = read()) == -1) { + if (i == 0) // At end of stream, so we should + i = -1; // return -1 , NOT 0. + break; + } + buf[off+i] = (byte)c; + } + return i; + } + + /** + * Skips over and discards n bytes of data from this stream. + */ + @Override + public long skip(long n) throws IOException { + long skipped = 0; + while (n-- > 0 && read() >= 0) + skipped++; + return skipped; + } + + /** + * Tests if this input stream supports marks. Currently this class + * does not support marks + */ + @Override + public boolean markSupported() { + return false; + } + + /** + * Returns the number of bytes that can be read from this input + * stream without blocking. The QP algorithm does not permit + * a priori knowledge of the number of bytes after decoding, so + * this method just invokes the available method + * of the original input stream. + */ + @Override + public int available() throws IOException { + // This is bogus ! We don't really know how much + // bytes are available *after* decoding + return in.available(); + } + + /**** begin TEST program + public static void main(String argv[]) throws Exception { + FileInputStream infile = new FileInputStream(argv[0]); + QPDecoderStream decoder = new QPDecoderStream(infile); + int c; + + while ((c = decoder.read()) != -1) + System.out.print((char)c); + System.out.println(); + } + *** end TEST program ****/ +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/QPEncoderStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/QPEncoderStream.java new file mode 100644 index 000000000..b518658f4 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/QPEncoderStream.java @@ -0,0 +1,220 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; + +/** + * This class implements a Quoted Printable Encoder. It is implemented as + * a FilterOutputStream, so one can just wrap this class around + * any output stream and write bytes into this filter. The Encoding + * is done as the bytes are written out. + * + * @author John Mani + */ + +public class QPEncoderStream extends FilterOutputStream { + private int count = 0; // number of bytes that have been output + private int bytesPerLine; // number of bytes per line + private boolean gotSpace = false; + private boolean gotCR = false; + + /** + * Create a QP encoder that encodes the specified input stream + * @param out the output stream + * @param bytesPerLine the number of bytes per line. The encoder + * inserts a CRLF sequence after this many number + * of bytes. + */ + public QPEncoderStream(OutputStream out, int bytesPerLine) { + super(out); + // Subtract 1 to account for the '=' in the soft-return + // at the end of a line + this.bytesPerLine = bytesPerLine - 1; + } + + /** + * Create a QP encoder that encodes the specified input stream. + * Inserts the CRLF sequence after outputting 76 bytes. + * @param out the output stream + */ + public QPEncoderStream(OutputStream out) { + this(out, 76); + } + + /** + * Encodes len bytes from the specified + * byte array starting at offset off to + * this output stream. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @exception IOException if an I/O error occurs. + */ + @Override + public void write(byte[] b, int off, int len) throws IOException { + for (int i = 0; i < len; i++) + write(b[off + i]); + } + + /** + * Encodes b.length bytes to this output stream. + * @param b the data to be written. + * @exception IOException if an I/O error occurs. + */ + @Override + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + /** + * Encodes the specified byte to this output stream. + * @param c the byte. + * @exception IOException if an I/O error occurs. + */ + @Override + public void write(int c) throws IOException { + c = c & 0xff; // Turn off the MSB. + if (gotSpace) { // previous character was + if (c == '\r' || c == '\n') + // if CR/LF, we need to encode the char + output(' ', true); + else // no encoding required, just output the char + output(' ', false); + gotSpace = false; + } + + if (c == '\r') { + gotCR = true; + outputCRLF(); + } else { + if (c == '\n') { + if (gotCR) + // This is a CRLF sequence, we already output the + // corresponding CRLF when we got the CR, so ignore this + ; + else + outputCRLF(); + } else if (c == ' ') { + gotSpace = true; + } else if (c < 040 || c >= 0177 || c == '=') + // Encoding required. + output(c, true); + else // No encoding required + output(c, false); + // whatever it was, it wasn't a CR + gotCR = false; + } + } + + /** + * Flushes this output stream and forces any buffered output bytes + * to be encoded out to the stream. + * @exception IOException if an I/O error occurs. + */ + @Override + public void flush() throws IOException { + if (gotSpace) { + output(' ', true); + gotSpace = false; + } + out.flush(); + } + + /** + * Forces any buffered output bytes to be encoded out to the stream + * and closes this output stream. + * + * @exception IOException for I/O errors + */ + @Override + public void close() throws IOException { + flush(); + out.close(); + } + + private void outputCRLF() throws IOException { + out.write('\r'); + out.write('\n'); + count = 0; + } + + // The encoding table + private final static char hex[] = { + '0','1', '2', '3', '4', '5', '6', '7', + '8','9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + protected void output(int c, boolean encode) throws IOException { + if (encode) { + if ((count += 3) > bytesPerLine) { + out.write('='); + out.write('\r'); + out.write('\n'); + count = 3; // set the next line's length + } + out.write('='); + out.write(hex[c >> 4]); + out.write(hex[c & 0xf]); + } else { + if (++count > bytesPerLine) { + out.write('='); + out.write('\r'); + out.write('\n'); + count = 1; // set the next line's length + } + out.write(c); + } + } + + /**** begin TEST program *** + public static void main(String argv[]) throws Exception { + FileInputStream infile = new FileInputStream(argv[0]); + QPEncoderStream encoder = new QPEncoderStream(System.out); + int c; + + while ((c = infile.read()) != -1) + encoder.write(c); + encoder.close(); + } + *** end TEST program ***/ +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/ReadableMime.java b/fine-third-default/fine-mail/src/com/sun/mail/util/ReadableMime.java new file mode 100644 index 000000000..d8a4c0601 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/ReadableMime.java @@ -0,0 +1,64 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.InputStream; + +import javax.mail.MessagingException; + +/** + * A Message or message Part whose data can be read as a MIME format + * stream. Note that the MIME stream will include both the headers + * and the body of the message or part. This should be the same data + * that is produced by the writeTo method, but in a readable form. + * + * @author Bill Shannon + * @since JavaMail 1.4.5 + */ +public interface ReadableMime { + /** + * Return the MIME format stream corresponding to this message part. + * + * @return the MIME format stream + * @exception MessagingException for failures + */ + public InputStream getMimeStream() throws MessagingException; +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/SharedByteArrayOutputStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/SharedByteArrayOutputStream.java new file mode 100644 index 000000000..4bb9fd33e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/SharedByteArrayOutputStream.java @@ -0,0 +1,64 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.InputStream; +import java.io.ByteArrayOutputStream; + +import javax.mail.util.SharedByteArrayInputStream; + +/** + * A ByteArrayOutputStream that allows us to share the byte array + * rather than copy it. Eventually could replace this with something + * that doesn't require a single contiguous byte array. + * + * @author Bill Shannon + * @since JavaMail 1.4.5 + */ +public class SharedByteArrayOutputStream extends ByteArrayOutputStream { + public SharedByteArrayOutputStream(int size) { + super(size); + } + + public InputStream toStream() { + return new SharedByteArrayInputStream(buf, 0, count); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/SocketConnectException.java b/fine-third-default/fine-mail/src/com/sun/mail/util/SocketConnectException.java new file mode 100644 index 000000000..a55c1ed21 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/SocketConnectException.java @@ -0,0 +1,129 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.IOException; + +/** + * An IOException that indicates a socket connection attempt failed. + * Unlike java.net.ConnectException, it includes details of what we + * were trying to connect to. + * + * @see java.net.ConnectException + * @author Bill Shannon + * @since JavaMail 1.5.0 + */ + +public class SocketConnectException extends IOException { + /** + * The socket host name. + */ + private String host; + /** + * The socket port. + */ + private int port; + /** + * The connection timeout. + */ + private int cto; + /** + * The generated serial id. + */ + private static final long serialVersionUID = 3997871560538755463L; + + /** + * Constructs a SocketConnectException. + * + * @param msg error message detail + * @param cause the underlying exception that indicates the failure + * @param host the host we were trying to connect to + * @param port the port we were trying to connect to + * @param cto the timeout for the connection attempt + */ + public SocketConnectException(String msg, Exception cause, + String host, int port, int cto) { + super(msg); + initCause(cause); + this.host = host; + this.port = port; + this.cto = cto; + } + + /** + * The exception that caused the failure. + * + * @return the exception + */ + public Exception getException() { + // the "cause" is always an Exception; see constructor above + Throwable t = getCause(); + assert t == null || t instanceof Exception; + return (Exception) t; + } + + /** + * The host we were trying to connect to. + * + * @return the host + */ + public String getHost() { + return host; + } + + /** + * The port we were trying to connect to. + * + * @return the port + */ + public int getPort() { + return port; + } + + /** + * The timeout used for the connection attempt. + * + * @return the connection timeout + */ + public int getConnectionTimeout() { + return cto; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/SocketFetcher.java b/fine-third-default/fine-mail/src/com/sun/mail/util/SocketFetcher.java new file mode 100644 index 000000000..1693cc57b --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/SocketFetcher.java @@ -0,0 +1,919 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.security.*; +import java.net.*; +import java.io.*; +import java.nio.channels.SocketChannel; +import java.nio.charset.StandardCharsets; +import java.lang.reflect.*; +import java.util.*; +import java.util.regex.*; +import java.util.logging.Level; +import java.security.cert.*; +import javax.net.*; +import javax.net.ssl.*; + +/** + * This class is used to get Sockets. Depending on the arguments passed + * it will either return a plain java.net.Socket or dynamically load + * the SocketFactory class specified in the classname param and return + * a socket created by that SocketFactory. + * + * @author Max Spivak + * @author Bill Shannon + */ +public class SocketFetcher { + + private static MailLogger logger = new MailLogger( + SocketFetcher.class, + "socket", + "DEBUG SocketFetcher", + PropUtil.getBooleanSystemProperty("mail.socket.debug", false), + System.out); + + // No one should instantiate this class. + private SocketFetcher() { + } + + /** + * This method returns a Socket. Properties control the use of + * socket factories and other socket characteristics. The properties + * used are: + *

      + *
    • prefix.socketFactory + *
    • prefix.socketFactory.class + *
    • prefix.socketFactory.fallback + *
    • prefix.socketFactory.port + *
    • prefix.ssl.socketFactory + *
    • prefix.ssl.socketFactory.class + *
    • prefix.ssl.socketFactory.port + *
    • prefix.timeout + *
    • prefix.connectiontimeout + *
    • prefix.localaddress + *
    • prefix.localport + *
    • prefix.usesocketchannels + *

    + * If we're making an SSL connection, the ssl.socketFactory + * properties are used first, if set.

    + * + * If the socketFactory property is set, the value is an + * instance of a SocketFactory class, not a string. The + * instance is used directly. If the socketFactory property + * is not set, the socketFactory.class property is considered. + * (Note that the SocketFactory property must be set using the + * put method, not the setProperty + * method.)

    + * + * If the socketFactory.class property isn't set, the socket + * returned is an instance of java.net.Socket connected to the + * given host and port. If the socketFactory.class property is set, + * it is expected to contain a fully qualified classname of a + * javax.net.SocketFactory subclass. In this case, the class is + * dynamically instantiated and a socket created by that + * SocketFactory is returned.

    + * + * If the socketFactory.fallback property is set to false, don't + * fall back to using regular sockets if the socket factory fails.

    + * + * The socketFactory.port specifies a port to use when connecting + * through the socket factory. If unset, the port argument will be + * used.

    + * + * If the connectiontimeout property is set, the timeout is passed + * to the socket connect method.

    + * + * If the timeout property is set, it is used to set the socket timeout. + *

    + * + * If the localaddress property is set, it's used as the local address + * to bind to. If the localport property is also set, it's used as the + * local port number to bind to.

    + * + * If the usesocketchannels property is set, and we create the Socket + * ourself, and the selection of other properties allows, create a + * SocketChannel and get the Socket from it. This allows us to later + * retrieve the SocketChannel from the Socket and use it with Select. + * + * @param host The host to connect to + * @param port The port to connect to at the host + * @param props Properties object containing socket properties + * @param prefix Property name prefix, e.g., "mail.imap" + * @param useSSL use the SSL socket factory as the default + * @return the Socket + * @exception IOException for I/O errors + */ + public static Socket getSocket(String host, int port, Properties props, + String prefix, boolean useSSL) + throws IOException { + + if (logger.isLoggable(Level.FINER)) + logger.finer("getSocket" + ", host " + host + ", port " + port + + ", prefix " + prefix + ", useSSL " + useSSL); + if (prefix == null) + prefix = "socket"; + if (props == null) + props = new Properties(); // empty + int cto = PropUtil.getIntProperty(props, + prefix + ".connectiontimeout", -1); + Socket socket = null; + String localaddrstr = props.getProperty(prefix + ".localaddress", null); + InetAddress localaddr = null; + if (localaddrstr != null) + localaddr = InetAddress.getByName(localaddrstr); + int localport = PropUtil.getIntProperty(props, + prefix + ".localport", 0); + + boolean fb = PropUtil.getBooleanProperty(props, + prefix + ".socketFactory.fallback", true); + + int sfPort = -1; + String sfErr = "unknown socket factory"; + int to = PropUtil.getIntProperty(props, prefix + ".timeout", -1); + try { + /* + * If using SSL, first look for SSL-specific class name or + * factory instance. + */ + SocketFactory sf = null; + String sfPortName = null; + if (useSSL) { + Object sfo = props.get(prefix + ".ssl.socketFactory"); + if (sfo instanceof SocketFactory) { + sf = (SocketFactory)sfo; + sfErr = "SSL socket factory instance " + sf; + } + if (sf == null) { + String sfClass = + props.getProperty(prefix + ".ssl.socketFactory.class"); + sf = getSocketFactory(sfClass); + sfErr = "SSL socket factory class " + sfClass; + } + sfPortName = ".ssl.socketFactory.port"; + } + + if (sf == null) { + Object sfo = props.get(prefix + ".socketFactory"); + if (sfo instanceof SocketFactory) { + sf = (SocketFactory)sfo; + sfErr = "socket factory instance " + sf; + } + if (sf == null) { + String sfClass = + props.getProperty(prefix + ".socketFactory.class"); + sf = getSocketFactory(sfClass); + sfErr = "socket factory class " + sfClass; + } + sfPortName = ".socketFactory.port"; + } + + // if we now have a socket factory, use it + if (sf != null) { + sfPort = PropUtil.getIntProperty(props, + prefix + sfPortName, -1); + + // if port passed in via property isn't valid, use param + if (sfPort == -1) + sfPort = port; + socket = createSocket(localaddr, localport, + host, sfPort, cto, to, props, prefix, sf, useSSL); + } + } catch (SocketTimeoutException sex) { + throw sex; + } catch (Exception ex) { + if (!fb) { + if (ex instanceof InvocationTargetException) { + Throwable t = + ((InvocationTargetException)ex).getTargetException(); + if (t instanceof Exception) + ex = (Exception)t; + } + if (ex instanceof IOException) + throw (IOException)ex; + throw new SocketConnectException("Using " + sfErr, ex, + host, sfPort, cto); + } + } + + if (socket == null) { + socket = createSocket(localaddr, localport, + host, port, cto, to, props, prefix, null, useSSL); + + } else { + if (to >= 0) { + if (logger.isLoggable(Level.FINEST)) + logger.finest("set socket read timeout " + to); + socket.setSoTimeout(to); + } + } + + return socket; + } + + public static Socket getSocket(String host, int port, Properties props, + String prefix) throws IOException { + return getSocket(host, port, props, prefix, false); + } + + /** + * Create a socket with the given local address and connected to + * the given host and port. Use the specified connection timeout + * and read timeout. + * If a socket factory is specified, use it. Otherwise, use the + * SSLSocketFactory if useSSL is true. + */ + private static Socket createSocket(InetAddress localaddr, int localport, + String host, int port, int cto, int to, + Properties props, String prefix, + SocketFactory sf, boolean useSSL) + throws IOException { + Socket socket = null; + + if (logger.isLoggable(Level.FINEST)) + logger.finest("create socket: prefix " + prefix + + ", localaddr " + localaddr + ", localport " + localport + + ", host " + host + ", port " + port + + ", connection timeout " + cto + ", timeout " + to + + ", socket factory " + sf + ", useSSL " + useSSL); + + String proxyHost = props.getProperty(prefix + ".proxy.host", null); + String proxyUser = props.getProperty(prefix + ".proxy.user", null); + String proxyPassword = props.getProperty(prefix + ".proxy.password", null); + int proxyPort = 80; + String socksHost = null; + int socksPort = 1080; + String err = null; + + if (proxyHost != null) { + int i = proxyHost.indexOf(':'); + if (i >= 0) { + try { + proxyPort = Integer.parseInt(proxyHost.substring(i + 1)); + } catch (NumberFormatException ex) { + // ignore it + } + proxyHost = proxyHost.substring(0, i); + } + proxyPort = PropUtil.getIntProperty(props, + prefix + ".proxy.port", proxyPort); + err = "Using web proxy host, port: " + proxyHost + ", " + proxyPort; + if (logger.isLoggable(Level.FINER)) { + logger.finer("web proxy host " + proxyHost + ", port " + proxyPort); + if (proxyUser != null) + logger.finer("web proxy user " + proxyUser + ", password " + + (proxyPassword == null ? "" : "")); + } + } else if ((socksHost = + props.getProperty(prefix + ".socks.host", null)) != null) { + int i = socksHost.indexOf(':'); + if (i >= 0) { + try { + socksPort = Integer.parseInt(socksHost.substring(i + 1)); + } catch (NumberFormatException ex) { + // ignore it + } + socksHost = socksHost.substring(0, i); + } + socksPort = PropUtil.getIntProperty(props, + prefix + ".socks.port", socksPort); + err = "Using SOCKS host, port: " + socksHost + ", " + socksPort; + if (logger.isLoggable(Level.FINER)) + logger.finer("socks host " + socksHost + ", port " + socksPort); + } + + if (sf != null && !(sf instanceof SSLSocketFactory)) + socket = sf.createSocket(); + if (socket == null) { + if (socksHost != null) { + socket = new Socket( + new java.net.Proxy(java.net.Proxy.Type.SOCKS, + new InetSocketAddress(socksHost, socksPort))); + } else if (PropUtil.getBooleanProperty(props, + prefix + ".usesocketchannels", false)) { + logger.finer("using SocketChannels"); + socket = SocketChannel.open().socket(); + } else + socket = new Socket(); + } + if (to >= 0) { + if (logger.isLoggable(Level.FINEST)) + logger.finest("set socket read timeout " + to); + socket.setSoTimeout(to); + } + int writeTimeout = PropUtil.getIntProperty(props, + prefix + ".writetimeout", -1); + if (writeTimeout != -1) { // wrap original + if (logger.isLoggable(Level.FINEST)) + logger.finest("set socket write timeout " + writeTimeout); + socket = new WriteTimeoutSocket(socket, writeTimeout); + } + if (localaddr != null) + socket.bind(new InetSocketAddress(localaddr, localport)); + try { + logger.finest("connecting..."); + if (proxyHost != null) + proxyConnect(socket, proxyHost, proxyPort, + proxyUser, proxyPassword, host, port, cto); + else if (cto >= 0) + socket.connect(new InetSocketAddress(host, port), cto); + else + socket.connect(new InetSocketAddress(host, port)); + logger.finest("success!"); + } catch (IOException ex) { + logger.log(Level.FINEST, "connection failed", ex); + throw new SocketConnectException(err, ex, host, port, cto); + } + + /* + * If we want an SSL connection and we didn't get an SSLSocket, + * wrap our plain Socket with an SSLSocket. + */ + if ((useSSL || sf instanceof SSLSocketFactory) && + !(socket instanceof SSLSocket)) { + String trusted; + SSLSocketFactory ssf; + if ((trusted = props.getProperty(prefix + ".ssl.trust")) != null) { + try { + MailSSLSocketFactory msf = new MailSSLSocketFactory(); + if (trusted.equals("*")) + msf.setTrustAllHosts(true); + else + msf.setTrustedHosts(trusted.split("\\s+")); + ssf = msf; + } catch (GeneralSecurityException gex) { + IOException ioex = new IOException( + "Can't create MailSSLSocketFactory"); + ioex.initCause(gex); + throw ioex; + } + } else if (sf instanceof SSLSocketFactory) + ssf = (SSLSocketFactory)sf; + else + ssf = (SSLSocketFactory)SSLSocketFactory.getDefault(); + socket = ssf.createSocket(socket, host, port, true); + sf = ssf; + } + + /* + * No matter how we created the socket, if it turns out to be an + * SSLSocket, configure it. + */ + configureSSLSocket(socket, host, props, prefix, sf); + + return socket; + } + + /** + * Return a socket factory of the specified class. + */ + private static SocketFactory getSocketFactory(String sfClass) + throws ClassNotFoundException, + NoSuchMethodException, + IllegalAccessException, + InvocationTargetException { + if (sfClass == null || sfClass.length() == 0) + return null; + + // dynamically load the class + + ClassLoader cl = getContextClassLoader(); + Class clsSockFact = null; + if (cl != null) { + try { + clsSockFact = Class.forName(sfClass, false, cl); + } catch (ClassNotFoundException cex) { } + } + if (clsSockFact == null) + clsSockFact = Class.forName(sfClass); + // get & invoke the getDefault() method + Method mthGetDefault = clsSockFact.getMethod("getDefault", + new Class[]{}); + SocketFactory sf = (SocketFactory) + mthGetDefault.invoke(new Object(), new Object[]{}); + return sf; + } + + /** + * Start TLS on an existing socket. + * Supports the "STARTTLS" command in many protocols. + * This version for compatibility with possible third party code + * that might've used this API even though it shouldn't. + * + * @param socket the existing socket + * @return the wrapped Socket + * @exception IOException for I/O errors + * @deprecated + */ + @Deprecated + public static Socket startTLS(Socket socket) throws IOException { + return startTLS(socket, new Properties(), "socket"); + } + + /** + * Start TLS on an existing socket. + * Supports the "STARTTLS" command in many protocols. + * This version for compatibility with possible third party code + * that might've used this API even though it shouldn't. + * + * @param socket the existing socket + * @param props the properties + * @param prefix the property prefix + * @return the wrapped Socket + * @exception IOException for I/O errors + * @deprecated + */ + @Deprecated + public static Socket startTLS(Socket socket, Properties props, + String prefix) throws IOException { + InetAddress a = socket.getInetAddress(); + String host = a.getHostName(); + return startTLS(socket, host, props, prefix); + } + + /** + * Start TLS on an existing socket. + * Supports the "STARTTLS" command in many protocols. + * + * @param socket the existing socket + * @param host the host the socket is connected to + * @param props the properties + * @param prefix the property prefix + * @return the wrapped Socket + * @exception IOException for I/O errors + */ + public static Socket startTLS(Socket socket, String host, Properties props, + String prefix) throws IOException { + int port = socket.getPort(); + if (logger.isLoggable(Level.FINER)) + logger.finer("startTLS host " + host + ", port " + port); + + String sfErr = "unknown socket factory"; + try { + SSLSocketFactory ssf = null; + SocketFactory sf = null; + + // first, look for an SSL socket factory + Object sfo = props.get(prefix + ".ssl.socketFactory"); + if (sfo instanceof SocketFactory) { + sf = (SocketFactory)sfo; + sfErr = "SSL socket factory instance " + sf; + } + if (sf == null) { + String sfClass = + props.getProperty(prefix + ".ssl.socketFactory.class"); + sf = getSocketFactory(sfClass); + sfErr = "SSL socket factory class " + sfClass; + } + if (sf != null && sf instanceof SSLSocketFactory) + ssf = (SSLSocketFactory)sf; + + // next, look for a regular socket factory that happens to be + // an SSL socket factory + if (ssf == null) { + sfo = props.get(prefix + ".socketFactory"); + if (sfo instanceof SocketFactory) { + sf = (SocketFactory)sfo; + sfErr = "socket factory instance " + sf; + } + if (sf == null) { + String sfClass = + props.getProperty(prefix + ".socketFactory.class"); + sf = getSocketFactory(sfClass); + sfErr = "socket factory class " + sfClass; + } + if (sf != null && sf instanceof SSLSocketFactory) + ssf = (SSLSocketFactory)sf; + } + + // finally, use the default SSL socket factory + if (ssf == null) { + String trusted; + if ((trusted = props.getProperty(prefix + ".ssl.trust")) != + null) { + try { + MailSSLSocketFactory msf = new MailSSLSocketFactory(); + if (trusted.equals("*")) + msf.setTrustAllHosts(true); + else + msf.setTrustedHosts(trusted.split("\\s+")); + ssf = msf; + sfErr = "mail SSL socket factory"; + } catch (GeneralSecurityException gex) { + IOException ioex = new IOException( + "Can't create MailSSLSocketFactory"); + ioex.initCause(gex); + throw ioex; + } + } else { + ssf = (SSLSocketFactory)SSLSocketFactory.getDefault(); + sfErr = "default SSL socket factory"; + } + } + + socket = ssf.createSocket(socket, host, port, true); + configureSSLSocket(socket, host, props, prefix, ssf); + } catch (Exception ex) { + if (ex instanceof InvocationTargetException) { + Throwable t = + ((InvocationTargetException)ex).getTargetException(); + if (t instanceof Exception) + ex = (Exception)t; + } + if (ex instanceof IOException) + throw (IOException)ex; + // wrap anything else before sending it on + IOException ioex = new IOException( + "Exception in startTLS using " + sfErr + + ": host, port: " + + host + ", " + port + + "; Exception: " + ex); + ioex.initCause(ex); + throw ioex; + } + return socket; + } + + /** + * Configure the SSL options for the socket (if it's an SSL socket), + * based on the mail..ssl.protocols and + * mail..ssl.ciphersuites properties. + * Check the identity of the server as specified by the + * mail..ssl.checkserveridentity property. + */ + private static void configureSSLSocket(Socket socket, String host, + Properties props, String prefix, SocketFactory sf) + throws IOException { + if (!(socket instanceof SSLSocket)) + return; + SSLSocket sslsocket = (SSLSocket)socket; + + String protocols = props.getProperty(prefix + ".ssl.protocols", null); + if (protocols != null) + sslsocket.setEnabledProtocols(stringArray(protocols)); + else { + /* + * The UW IMAP server insists on at least the TLSv1 + * protocol for STARTTLS, and won't accept the old SSLv2 + * or SSLv3 protocols. Here we enable only the non-SSL + * protocols. XXX - this should probably be parameterized. + */ + String[] prots = sslsocket.getEnabledProtocols(); + if (logger.isLoggable(Level.FINER)) + logger.finer("SSL enabled protocols before " + + Arrays.asList(prots)); + List eprots = new ArrayList<>(); + for (int i = 0; i < prots.length; i++) { + if (prots[i] != null && !prots[i].startsWith("SSL")) + eprots.add(prots[i]); + } + sslsocket.setEnabledProtocols( + eprots.toArray(new String[eprots.size()])); + } + String ciphers = props.getProperty(prefix + ".ssl.ciphersuites", null); + if (ciphers != null) + sslsocket.setEnabledCipherSuites(stringArray(ciphers)); + if (logger.isLoggable(Level.FINER)) { + logger.finer("SSL enabled protocols after " + + Arrays.asList(sslsocket.getEnabledProtocols())); + logger.finer("SSL enabled ciphers after " + + Arrays.asList(sslsocket.getEnabledCipherSuites())); + } + + /* + * Force the handshake to be done now so that we can report any + * errors (e.g., certificate errors) to the caller of the startTLS + * method. + */ + sslsocket.startHandshake(); + + /* + * Check server identity and trust. + */ + boolean idCheck = PropUtil.getBooleanProperty(props, + prefix + ".ssl.checkserveridentity", false); + if (idCheck) + checkServerIdentity(host, sslsocket); + if (sf instanceof MailSSLSocketFactory) { + MailSSLSocketFactory msf = (MailSSLSocketFactory)sf; + if (!msf.isServerTrusted(host, sslsocket)) { + throw cleanupAndThrow(sslsocket, + new IOException("Server is not trusted: " + host)); + } + } + } + + private static IOException cleanupAndThrow(Socket socket, IOException ife) { + try { + socket.close(); + } catch (Throwable thr) { + if (isRecoverable(thr)) { + ife.addSuppressed(thr); + } else { + thr.addSuppressed(ife); + if (thr instanceof Error) { + throw (Error) thr; + } + if (thr instanceof RuntimeException) { + throw (RuntimeException) thr; + } + throw new RuntimeException("unexpected exception", thr); + } + } + return ife; + } + + private static boolean isRecoverable(Throwable t) { + return (t instanceof Exception) || (t instanceof LinkageError); + } + + /** + * Check the server from the Socket connection against the server name(s) + * as expressed in the server certificate (RFC 2595 check). + * + * @param server name of the server expected + * @param sslSocket SSLSocket connected to the server + * @exception IOException if we can't verify identity of server + */ + private static void checkServerIdentity(String server, SSLSocket sslSocket) + throws IOException { + + // Check against the server name(s) as expressed in server certificate + try { + java.security.cert.Certificate[] certChain = + sslSocket.getSession().getPeerCertificates(); + if (certChain != null && certChain.length > 0 && + certChain[0] instanceof X509Certificate && + matchCert(server, (X509Certificate)certChain[0])) + return; + } catch (SSLPeerUnverifiedException e) { + sslSocket.close(); + IOException ioex = new IOException( + "Can't verify identity of server: " + server); + ioex.initCause(e); + throw ioex; + } + + // If we get here, there is nothing to consider the server as trusted. + sslSocket.close(); + throw new IOException("Can't verify identity of server: " + server); + } + + /** + * Do any of the names in the cert match the server name? + * + * @param server name of the server expected + * @param cert X509Certificate to get the subject's name from + * @return true if it matches + */ + private static boolean matchCert(String server, X509Certificate cert) { + if (logger.isLoggable(Level.FINER)) + logger.finer("matchCert server " + + server + ", cert " + cert); + + /* + * First, try to use sun.security.util.HostnameChecker, + * which exists in Sun's JDK starting with 1.4.1. + * We use reflection to access it in case it's not available + * in the JDK we're running on. + */ + try { + Class hnc = Class.forName("sun.security.util.HostnameChecker"); + // invoke HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP) + // HostnameChecker.TYPE_LDAP == 2 + // LDAP requires the same regex handling as we need + Method getInstance = hnc.getMethod("getInstance", + new Class[] { byte.class }); + Object hostnameChecker = getInstance.invoke(new Object(), + new Object[] { Byte.valueOf((byte)2) }); + + // invoke hostnameChecker.match( server, cert) + if (logger.isLoggable(Level.FINER)) + logger.finer("using sun.security.util.HostnameChecker"); + Method match = hnc.getMethod("match", + new Class[] { String.class, X509Certificate.class }); + try { + match.invoke(hostnameChecker, new Object[] { server, cert }); + return true; + } catch (InvocationTargetException cex) { + logger.log(Level.FINER, "HostnameChecker FAIL", cex); + return false; + } + } catch (Exception ex) { + logger.log(Level.FINER, "NO sun.security.util.HostnameChecker", ex); + // ignore failure and continue below + } + + /* + * Lacking HostnameChecker, we implement a crude version of + * the same checks ourselves. + */ + try { + /* + * Check each of the subjectAltNames. + * XXX - only checks DNS names, should also handle + * case where server name is a literal IP address + */ + Collection> names = cert.getSubjectAlternativeNames(); + if (names != null) { + boolean foundName = false; + for (Iterator> it = names.iterator(); it.hasNext(); ) { + List nameEnt = it.next(); + Integer type = (Integer)nameEnt.get(0); + if (type.intValue() == 2) { // 2 == dNSName + foundName = true; + String name = (String)nameEnt.get(1); + if (logger.isLoggable(Level.FINER)) + logger.finer("found name: " + name); + if (matchServer(server, name)) + return true; + } + } + if (foundName) // found a name, but no match + return false; + } + } catch (CertificateParsingException ex) { + // ignore it + } + + // XXX - following is a *very* crude parse of the name and ignores + // all sorts of important issues such as quoting + Pattern p = Pattern.compile("CN=([^,]*)"); + Matcher m = p.matcher(cert.getSubjectX500Principal().getName()); + if (m.find() && matchServer(server, m.group(1).trim())) + return true; + + return false; + } + + /** + * Does the server we're expecting to connect to match the + * given name from the server's certificate? + * + * @param server name of the server expected + * @param name name from the server's certificate + */ + private static boolean matchServer(String server, String name) { + if (logger.isLoggable(Level.FINER)) + logger.finer("match server " + server + " with " + name); + if (name.startsWith("*.")) { + // match "foo.example.com" with "*.example.com" + String tail = name.substring(2); + if (tail.length() == 0) + return false; + int off = server.length() - tail.length(); + if (off < 1) + return false; + // if tail matches and is preceeded by "." + return server.charAt(off - 1) == '.' && + server.regionMatches(true, off, tail, 0, tail.length()); + } else + return server.equalsIgnoreCase(name); + } + + /** + * Use the HTTP CONNECT protocol to connect to a + * site through an HTTP proxy server.

    + * + * Protocol is roughly: + *

    +     * CONNECT : HTTP/1.1
    +     * Host: :
    +     * 
    +     *
    +     * HTTP/1.1 200 Connect established
    +     * 
    +     * 
    +     * 
    + */ + private static void proxyConnect(Socket socket, + String proxyHost, int proxyPort, + String proxyUser, String proxyPassword, + String host, int port, int cto) + throws IOException { + if (logger.isLoggable(Level.FINE)) + logger.fine("connecting through proxy " + + proxyHost + ":" + proxyPort + " to " + + host + ":" + port); + if (cto >= 0) + socket.connect(new InetSocketAddress(proxyHost, proxyPort), cto); + else + socket.connect(new InetSocketAddress(proxyHost, proxyPort)); + PrintStream os = new PrintStream(socket.getOutputStream(), false, + StandardCharsets.UTF_8.name()); + StringBuilder requestBuilder = new StringBuilder(); + requestBuilder.append("CONNECT ").append(host).append(":").append(port). + append(" HTTP/1.1\r\n"); + requestBuilder.append("Host: ").append(host).append(":").append(port). + append("\r\n"); + if (proxyUser != null && proxyPassword != null) { + byte[] upbytes = (proxyUser + ':' + proxyPassword). + getBytes(StandardCharsets.UTF_8); + String proxyHeaderValue = new String( + BASE64EncoderStream.encode(upbytes), + StandardCharsets.US_ASCII); + requestBuilder.append("Proxy-Authorization: Basic "). + append(proxyHeaderValue).append("\r\n"); + } + requestBuilder.append("Proxy-Connection: keep-alive\r\n\r\n"); + os.print(requestBuilder.toString()); + os.flush(); + BufferedReader r = new BufferedReader(new InputStreamReader( + socket.getInputStream(), StandardCharsets.UTF_8)); + String line; + boolean first = true; + while ((line = r.readLine()) != null) { + if (line.length() == 0) + break; + logger.finest(line); + if (first) { + StringTokenizer st = new StringTokenizer(line); + String http = st.nextToken(); + String code = st.nextToken(); + if (!code.equals("200")) { + try { + socket.close(); + } catch (IOException ioex) { + // ignored + } + ConnectException ex = new ConnectException( + "connection through proxy " + + proxyHost + ":" + proxyPort + " to " + + host + ":" + port + " failed: " + line); + logger.log(Level.FINE, "connect failed", ex); + throw ex; + } + first = false; + } + } + } + + /** + * Parse a string into whitespace separated tokens + * and return the tokens in an array. + */ + private static String[] stringArray(String s) { + StringTokenizer st = new StringTokenizer(s); + List tokens = new ArrayList<>(); + while (st.hasMoreTokens()) + tokens.add(st.nextToken()); + return tokens.toArray(new String[tokens.size()]); + } + + /** + * Convenience method to get our context class loader. + * Assert any privileges we might have and then call the + * Thread.getContextClassLoader method. + */ + private static ClassLoader getContextClassLoader() { + return + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public ClassLoader run() { + ClassLoader cl = null; + try { + cl = Thread.currentThread().getContextClassLoader(); + } catch (SecurityException ex) { } + return cl; + } + }); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/TraceInputStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/TraceInputStream.java new file mode 100644 index 000000000..576b8019d --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/TraceInputStream.java @@ -0,0 +1,164 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; +import java.util.logging.Level; + +/** + * This class is a FilterInputStream that writes the bytes + * being read from the given input stream into the given output + * stream. This class is typically used to provide a trace of + * the data that is being retrieved from an input stream. + * + * @author John Mani + */ + +public class TraceInputStream extends FilterInputStream { + private boolean trace = false; + private boolean quote = false; + private OutputStream traceOut; + + /** + * Creates an input stream filter built on top of the specified + * input stream. + * + * @param in the underlying input stream. + * @param logger log trace here + */ + public TraceInputStream(InputStream in, MailLogger logger) { + super(in); + this.trace = logger.isLoggable(Level.FINEST); + this.traceOut = new LogOutputStream(logger); + } + + /** + * Creates an input stream filter built on top of the specified + * input stream. + * + * @param in the underlying input stream. + * @param traceOut the trace stream. + */ + public TraceInputStream(InputStream in, OutputStream traceOut) { + super(in); + this.traceOut = traceOut; + } + + /** + * Set trace mode. + * @param trace the trace mode + */ + public void setTrace(boolean trace) { + this.trace = trace; + } + + /** + * Set quote mode. + * @param quote the quote mode + */ + public void setQuote(boolean quote) { + this.quote = quote; + } + + /** + * Reads the next byte of data from this input stream. Returns + * -1 if no data is available. Writes out the read + * byte into the trace stream, if trace mode is true + */ + @Override + public int read() throws IOException { + int b = in.read(); + if (trace && b != -1) { + if (quote) + writeByte(b); + else + traceOut.write(b); + } + return b; + } + + /** + * Reads up to len bytes of data from this input stream + * into an array of bytes. Returns -1 if no more data + * is available. Writes out the read bytes into the trace stream, if + * trace mode is true + */ + @Override + public int read(byte b[], int off, int len) throws IOException { + int count = in.read(b, off, len); + if (trace && count != -1) { + if (quote) { + for (int i = 0; i < count; i++) + writeByte(b[off + i]); + } else + traceOut.write(b, off, count); + } + return count; + } + + /** + * Write a byte in a way that every byte value is printable ASCII. + */ + private final void writeByte(int b) throws IOException { + b &= 0xff; + if (b > 0x7f) { + traceOut.write('M'); + traceOut.write('-'); + b &= 0x7f; + } + if (b == '\r') { + traceOut.write('\\'); + traceOut.write('r'); + } else if (b == '\n') { + traceOut.write('\\'); + traceOut.write('n'); + traceOut.write('\n'); + } else if (b == '\t') { + traceOut.write('\\'); + traceOut.write('t'); + } else if (b < ' ') { + traceOut.write('^'); + traceOut.write('@' + b); + } else { + traceOut.write(b); + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/TraceOutputStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/TraceOutputStream.java new file mode 100644 index 000000000..95595d449 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/TraceOutputStream.java @@ -0,0 +1,170 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; +import java.util.logging.Level; + +/** + * This class is a subclass of DataOutputStream that copies the + * data being written into the DataOutputStream into another output + * stream. This class is used here to provide a debug trace of the + * stuff thats being written out into the DataOutputStream. + * + * @author John Mani + */ + +public class TraceOutputStream extends FilterOutputStream { + private boolean trace = false; + private boolean quote = false; + private OutputStream traceOut; + + /** + * Creates an output stream filter built on top of the specified + * underlying output stream. + * + * @param out the underlying output stream. + * @param logger log trace here + */ + public TraceOutputStream(OutputStream out, MailLogger logger) { + super(out); + this.trace = logger.isLoggable(Level.FINEST); + this.traceOut = new LogOutputStream(logger);; + } + + /** + * Creates an output stream filter built on top of the specified + * underlying output stream. + * + * @param out the underlying output stream. + * @param traceOut the trace stream. + */ + public TraceOutputStream(OutputStream out, OutputStream traceOut) { + super(out); + this.traceOut = traceOut; + } + + /** + * Set the trace mode. + * + * @param trace the trace mode + */ + public void setTrace(boolean trace) { + this.trace = trace; + } + + /** + * Set quote mode. + * @param quote the quote mode + */ + public void setQuote(boolean quote) { + this.quote = quote; + } + + /** + * Writes the specified byte to this output stream. + * Writes out the byte into the trace stream if the trace mode + * is true + * + * @param b the byte to write + * @exception IOException for I/O errors + */ + @Override + public void write(int b) throws IOException { + if (trace) { + if (quote) + writeByte(b); + else + traceOut.write(b); + } + out.write(b); + } + + /** + * Writes b.length bytes to this output stream. + * Writes out the bytes into the trace stream if the trace + * mode is true + * + * @param b bytes to write + * @param off offset in array + * @param len number of bytes to write + * @exception IOException for I/O errors + */ + @Override + public void write(byte b[], int off, int len) throws IOException { + if (trace) { + if (quote) { + for (int i = 0; i < len; i++) + writeByte(b[off + i]); + } else + traceOut.write(b, off, len); + } + out.write(b, off, len); + } + + /** + * Write a byte in a way that every byte value is printable ASCII. + */ + private final void writeByte(int b) throws IOException { + b &= 0xff; + if (b > 0x7f) { + traceOut.write('M'); + traceOut.write('-'); + b &= 0x7f; + } + if (b == '\r') { + traceOut.write('\\'); + traceOut.write('r'); + } else if (b == '\n') { + traceOut.write('\\'); + traceOut.write('n'); + traceOut.write('\n'); + } else if (b == '\t') { + traceOut.write('\\'); + traceOut.write('t'); + } else if (b < ' ') { + traceOut.write('^'); + traceOut.write('@' + b); + } else { + traceOut.write(b); + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/UUDecoderStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/UUDecoderStream.java new file mode 100644 index 000000000..8717ae9fd --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/UUDecoderStream.java @@ -0,0 +1,358 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; + +/** + * This class implements a UUDecoder. It is implemented as + * a FilterInputStream, so one can just wrap this class around + * any input stream and read bytes from this filter. The decoding + * is done as the bytes are read out. + * + * @author John Mani + * @author Bill Shannon + */ + +public class UUDecoderStream extends FilterInputStream { + private String name; + private int mode; + + private byte[] buffer = new byte[45]; // max decoded chars in a line = 45 + private int bufsize = 0; // size of the cache + private int index = 0; // index into the cache + private boolean gotPrefix = false; + private boolean gotEnd = false; + private LineInputStream lin; + private boolean ignoreErrors; + private boolean ignoreMissingBeginEnd; + private String readAhead; + + /** + * Create a UUdecoder that decodes the specified input stream. + * The System property mail.mime.uudecode.ignoreerrors + * controls whether errors in the encoded data cause an exception + * or are ignored. The default is false (errors cause exception). + * The System property mail.mime.uudecode.ignoremissingbeginend + * controls whether a missing begin or end line cause an exception + * or are ignored. The default is false (errors cause exception). + * @param in the input stream + */ + public UUDecoderStream(InputStream in) { + super(in); + lin = new LineInputStream(in); + // default to false + ignoreErrors = PropUtil.getBooleanSystemProperty( + "mail.mime.uudecode.ignoreerrors", false); + // default to false + ignoreMissingBeginEnd = PropUtil.getBooleanSystemProperty( + "mail.mime.uudecode.ignoremissingbeginend", false); + } + + /** + * Create a UUdecoder that decodes the specified input stream. + * @param in the input stream + * @param ignoreErrors ignore errors? + * @param ignoreMissingBeginEnd ignore missing begin or end? + */ + public UUDecoderStream(InputStream in, boolean ignoreErrors, + boolean ignoreMissingBeginEnd) { + super(in); + lin = new LineInputStream(in); + this.ignoreErrors = ignoreErrors; + this.ignoreMissingBeginEnd = ignoreMissingBeginEnd; + } + + /** + * Read the next decoded byte from this input stream. The byte + * is returned as an int in the range 0 + * to 255. If no byte is available because the end of + * the stream has been reached, the value -1 is returned. + * This method blocks until input data is available, the end of the + * stream is detected, or an exception is thrown. + * + * @return next byte of data, or -1 if the end of + * stream is reached. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterInputStream#in + */ + @Override + public int read() throws IOException { + if (index >= bufsize) { + readPrefix(); + if (!decode()) + return -1; + index = 0; // reset index into buffer + } + return buffer[index++] & 0xff; // return lower byte + } + + @Override + public int read(byte[] buf, int off, int len) throws IOException { + int i, c; + for (i = 0; i < len; i++) { + if ((c = read()) == -1) { + if (i == 0) // At end of stream, so we should + i = -1; // return -1, NOT 0. + break; + } + buf[off+i] = (byte)c; + } + return i; + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public int available() throws IOException { + // This is only an estimate, since in.available() + // might include CRLFs too .. + return ((in.available() * 3)/4 + (bufsize-index)); + } + + /** + * Get the "name" field from the prefix. This is meant to + * be the pathname of the decoded file + * + * @return name of decoded file + * @exception IOException if an I/O error occurs. + */ + public String getName() throws IOException { + readPrefix(); + return name; + } + + /** + * Get the "mode" field from the prefix. This is the permission + * mode of the source file. + * + * @return permission mode of source file + * @exception IOException if an I/O error occurs. + */ + public int getMode() throws IOException { + readPrefix(); + return mode; + } + + /** + * UUencoded streams start off with the line: + * "begin " + * Search for this prefix and gobble it up. + */ + private void readPrefix() throws IOException { + if (gotPrefix) // got the prefix + return; + + mode = 0666; // defaults, overridden below + name = "encoder.buf"; // same default used by encoder + String line; + for (;;) { + // read till we get the prefix: "begin MODE FILENAME" + line = lin.readLine(); // NOTE: readLine consumes CRLF pairs too + if (line == null) { + if (!ignoreMissingBeginEnd) + throw new DecodingException("UUDecoder: Missing begin"); + // at EOF, fake it + gotPrefix = true; + gotEnd = true; + break; + } + if (line.regionMatches(false, 0, "begin", 0, 5)) { + try { + mode = Integer.parseInt(line.substring(6,9)); + } catch (NumberFormatException ex) { + if (!ignoreErrors) + throw new DecodingException( + "UUDecoder: Error in mode: " + ex.toString()); + } + if (line.length() > 10) { + name = line.substring(10); + } else { + if (!ignoreErrors) + throw new DecodingException( + "UUDecoder: Missing name: " + line); + } + gotPrefix = true; + break; + } else if (ignoreMissingBeginEnd && line.length() != 0) { + int count = line.charAt(0); + count = (count - ' ') & 0x3f; + int need = ((count * 8)+5)/6; + if (need == 0 || line.length() >= need + 1) { + /* + * Looks like a legitimate encoded line. + * Pretend we saw the "begin" line and + * save this line for later processing in + * decode(). + */ + readAhead = line; + gotPrefix = true; // fake it + break; + } + } + } + } + + private boolean decode() throws IOException { + + if (gotEnd) + return false; + bufsize = 0; + int count = 0; + String line; + for (;;) { + /* + * If we ignored a missing "begin", the first line + * will be saved in readAhead. + */ + if (readAhead != null) { + line = readAhead; + readAhead = null; + } else + line = lin.readLine(); + + /* + * Improperly encoded data sometimes omits the zero length + * line that starts with a space character, we detect the + * following "end" line here. + */ + if (line == null) { + if (!ignoreMissingBeginEnd) + throw new DecodingException( + "UUDecoder: Missing end at EOF"); + gotEnd = true; + return false; + } + if (line.equals("end")) { + gotEnd = true; + return false; + } + if (line.length() == 0) + continue; + count = line.charAt(0); + if (count < ' ') { + if (!ignoreErrors) + throw new DecodingException( + "UUDecoder: Buffer format error"); + continue; + } + + /* + * The first character in a line is the number of original (not + * the encoded atoms) characters in the line. Note that all the + * code below has to handle the character that indicates + * end of encoded stream. + */ + count = (count - ' ') & 0x3f; + + if (count == 0) { + line = lin.readLine(); + if (line == null || !line.equals("end")) { + if (!ignoreMissingBeginEnd) + throw new DecodingException( + "UUDecoder: Missing End after count 0 line"); + } + gotEnd = true; + return false; + } + + int need = ((count * 8)+5)/6; +//System.out.println("count " + count + ", need " + need + ", len " + line.length()); + if (line.length() < need + 1) { + if (!ignoreErrors) + throw new DecodingException( + "UUDecoder: Short buffer error"); + continue; + } + + // got a line we're committed to, break out and decode it + break; + } + + int i = 1; + byte a, b; + /* + * A correct uuencoder always encodes 3 characters at a time, even + * if there aren't 3 characters left. But since some people out + * there have broken uuencoders we handle the case where they + * don't include these "unnecessary" characters. + */ + while (bufsize < count) { + // continue decoding until we get 'count' decoded chars + a = (byte)((line.charAt(i++) - ' ') & 0x3f); + b = (byte)((line.charAt(i++) - ' ') & 0x3f); + buffer[bufsize++] = (byte)(((a << 2) & 0xfc) | ((b >>> 4) & 3)); + + if (bufsize < count) { + a = b; + b = (byte)((line.charAt(i++) - ' ') & 0x3f); + buffer[bufsize++] = + (byte)(((a << 4) & 0xf0) | ((b >>> 2) & 0xf)); + } + + if (bufsize < count) { + a = b; + b = (byte)((line.charAt(i++) - ' ') & 0x3f); + buffer[bufsize++] = (byte)(((a << 6) & 0xc0) | (b & 0x3f)); + } + } + return true; + } + + /*** begin TEST program ***** + public static void main(String argv[]) throws Exception { + FileInputStream infile = new FileInputStream(argv[0]); + UUDecoderStream decoder = new UUDecoderStream(infile); + int c; + + try { + while ((c = decoder.read()) != -1) + System.out.write(c); + System.out.flush(); + } catch (Exception e) { + e.printStackTrace(); + } + } + **** end TEST program ****/ +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/UUEncoderStream.java b/fine-third-default/fine-mail/src/com/sun/mail/util/UUEncoderStream.java new file mode 100644 index 000000000..76e5d0c34 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/UUEncoderStream.java @@ -0,0 +1,229 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; + +/** + * This class implements a UUEncoder. It is implemented as + * a FilterOutputStream, so one can just wrap this class around + * any output stream and write bytes into this filter. The Encoding + * is done as the bytes are written out. + * + * @author John Mani + */ + +public class UUEncoderStream extends FilterOutputStream { + private byte[] buffer; // cache of bytes that are yet to be encoded + private int bufsize = 0; // size of the cache + private boolean wrotePrefix = false; + private boolean wroteSuffix = false; + + private String name; // name of file + private int mode; // permissions mode + + /** + * Create a UUencoder that encodes the specified input stream + * @param out the output stream + */ + public UUEncoderStream(OutputStream out) { + this(out, "encoder.buf", 0644); + } + + /** + * Create a UUencoder that encodes the specified input stream + * @param out the output stream + * @param name Specifies a name for the encoded buffer + */ + public UUEncoderStream(OutputStream out, String name) { + this(out, name, 0644); + } + + /** + * Create a UUencoder that encodes the specified input stream + * @param out the output stream + * @param name Specifies a name for the encoded buffer + * @param mode Specifies permission mode for the encoded buffer + */ + public UUEncoderStream(OutputStream out, String name, int mode) { + super(out); + this.name = name; + this.mode = mode; + buffer = new byte[45]; + } + + /** + * Set up the buffer name and permission mode. + * This method has any effect only if it is invoked before + * you start writing into the output stream + * + * @param name the buffer name + * @param mode the permission mode + */ + public void setNameMode(String name, int mode) { + this.name = name; + this.mode = mode; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + for (int i = 0; i < len; i++) + write(b[off + i]); + } + + @Override + public void write(byte[] data) throws IOException { + write(data, 0, data.length); + } + + @Override + public void write(int c) throws IOException { + /* buffer up characters till we get a line's worth, then encode + * and write them out. Max number of characters allowed per + * line is 45. + */ + buffer[bufsize++] = (byte)c; + if (bufsize == 45) { + writePrefix(); + encode(); + bufsize = 0; + } + } + + @Override + public void flush() throws IOException { + if (bufsize > 0) { // If there's unencoded characters in the buffer + writePrefix(); + encode(); // .. encode them + bufsize = 0; + } + writeSuffix(); + out.flush(); + } + + @Override + public void close() throws IOException { + flush(); + out.close(); + } + + /** + * Write out the prefix: "begin " + */ + private void writePrefix() throws IOException { + if (!wrotePrefix) { + // name should be ASCII, but who knows... + PrintStream ps = new PrintStream(out, false, "utf-8"); + ps.format("begin %o %s%n", mode, name); + ps.flush(); + wrotePrefix = true; + } + } + + /** + * Write a single line containing space and the suffix line + * containing the single word "end" (terminated by a newline) + */ + private void writeSuffix() throws IOException { + if (!wroteSuffix) { + PrintStream ps = new PrintStream(out, false, "us-ascii"); + ps.println(" \nend"); + ps.flush(); + wroteSuffix = true; + } + } + + /** + * Encode a line. + * Start off with the character count, followed by the encoded atoms + * and terminate with LF. (or is it CRLF or the local line-terminator ?) + * Take three bytes and encodes them into 4 characters + * If bufsize if not a multiple of 3, the remaining bytes are filled + * with '1'. This insures that the last line won't end in spaces + * and potentiallly be truncated. + */ + private void encode() throws IOException { + byte a, b, c; + int c1, c2, c3, c4; + int i = 0; + + // Start off with the count of characters in the line + out.write((bufsize & 0x3f) + ' '); + + while (i < bufsize) { + a = buffer[i++]; + if (i < bufsize) { + b = buffer[i++]; + if (i < bufsize) + c = buffer[i++]; + else // default c to 1 + c = 1; + } + else { // default b & c to 1 + b = 1; + c = 1; + } + + c1 = (a >>> 2) & 0x3f; + c2 = ((a << 4) & 0x30) | ((b >>> 4) & 0xf); + c3 = ((b << 2) & 0x3c) | ((c >>> 6) & 0x3); + c4 = c & 0x3f; + out.write(c1 + ' '); + out.write(c2 + ' '); + out.write(c3 + ' '); + out.write(c4 + ' '); + } + // Terminate with LF. (should it be CRLF or local line-terminator ?) + out.write('\n'); + } + + /**** begin TEST program ***** + public static void main(String argv[]) throws Exception { + FileInputStream infile = new FileInputStream(argv[0]); + UUEncoderStream encoder = new UUEncoderStream(System.out); + int c; + + while ((c = infile.read()) != -1) + encoder.write(c); + encoder.close(); + } + **** end TEST program *****/ +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/WriteTimeoutSocket.java b/fine-third-default/fine-mail/src/com/sun/mail/util/WriteTimeoutSocket.java new file mode 100644 index 000000000..01884f1bb --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/WriteTimeoutSocket.java @@ -0,0 +1,425 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util; + +import java.io.*; +import java.net.*; +import java.util.concurrent.*; +import java.util.Collections; +import java.util.Set; +import java.nio.channels.SocketChannel; +import java.lang.reflect.*; + +/** + * A special Socket that uses a ScheduledExecutorService to + * implement timeouts for writes. The write timeout is specified + * (in milliseconds) when the WriteTimeoutSocket is created. + * + * @author Bill Shannon + */ +public class WriteTimeoutSocket extends Socket { + + // delegate all operations to this socket + private final Socket socket; + // to schedule task to cancel write after timeout + private final ScheduledExecutorService ses; + // the timeout, in milliseconds + private final int timeout; + + public WriteTimeoutSocket(Socket socket, int timeout) throws IOException { + this.socket = socket; + // XXX - could share executor with all instances? + this.ses = Executors.newScheduledThreadPool(1); + this.timeout = timeout; + } + + public WriteTimeoutSocket(int timeout) throws IOException { + this(new Socket(), timeout); + } + + public WriteTimeoutSocket(InetAddress address, int port, int timeout) + throws IOException { + this(timeout); + socket.connect(new InetSocketAddress(address, port)); + } + + public WriteTimeoutSocket(InetAddress address, int port, + InetAddress localAddress, int localPort, int timeout) + throws IOException { + this(timeout); + socket.bind(new InetSocketAddress(localAddress, localPort)); + socket.connect(new InetSocketAddress(address, port)); + } + + public WriteTimeoutSocket(String host, int port, int timeout) + throws IOException { + this(timeout); + socket.connect(new InetSocketAddress(host, port)); + } + + public WriteTimeoutSocket(String host, int port, + InetAddress localAddress, int localPort, int timeout) + throws IOException { + this(timeout); + socket.bind(new InetSocketAddress(localAddress, localPort)); + socket.connect(new InetSocketAddress(host, port)); + } + + // override all Socket methods and delegate to underlying Socket + + @Override + public void connect(SocketAddress remote) throws IOException { + socket.connect(remote, 0); + } + + @Override + public void connect(SocketAddress remote, int timeout) throws IOException { + socket.connect(remote, timeout); + } + + @Override + public void bind(SocketAddress local) throws IOException { + socket.bind(local); + } + + @Override + public SocketAddress getRemoteSocketAddress() { + return socket.getRemoteSocketAddress(); + } + + @Override + public SocketAddress getLocalSocketAddress() { + return socket.getLocalSocketAddress(); + } + + @Override + public void setPerformancePreferences(int connectionTime, int latency, + int bandwidth) { + socket.setPerformancePreferences(connectionTime, latency, bandwidth); + } + + @Override + public SocketChannel getChannel() { + return socket.getChannel(); + } + + @Override + public InetAddress getInetAddress() { + return socket.getInetAddress(); + } + + @Override + public InetAddress getLocalAddress() { + return socket.getLocalAddress(); + } + + @Override + public int getPort() { + return socket.getPort(); + } + + @Override + public int getLocalPort() { + return socket.getLocalPort(); + } + + @Override + public InputStream getInputStream() throws IOException { + return socket.getInputStream(); + } + + @Override + public synchronized OutputStream getOutputStream() throws IOException { + // wrap the returned stream to implement write timeout + return new TimeoutOutputStream(socket.getOutputStream(), ses, timeout); + } + + @Override + public void setTcpNoDelay(boolean on) throws SocketException { + socket.setTcpNoDelay(on); + } + + @Override + public boolean getTcpNoDelay() throws SocketException { + return socket.getTcpNoDelay(); + } + + @Override + public void setSoLinger(boolean on, int linger) throws SocketException { + socket.setSoLinger(on, linger); + } + + @Override + public int getSoLinger() throws SocketException { + return socket.getSoLinger(); + } + + @Override + public void sendUrgentData(int data) throws IOException { + socket.sendUrgentData(data); + } + + @Override + public void setOOBInline(boolean on) throws SocketException { + socket.setOOBInline(on); + } + + @Override + public boolean getOOBInline() throws SocketException { + return socket.getOOBInline(); + } + + @Override + public void setSoTimeout(int timeout) throws SocketException { + socket.setSoTimeout(timeout); + } + + @Override + public int getSoTimeout() throws SocketException { + return socket.getSoTimeout(); + } + + @Override + public void setSendBufferSize(int size) throws SocketException { + socket.setSendBufferSize(size); + } + + @Override + public int getSendBufferSize() throws SocketException { + return socket.getSendBufferSize(); + } + + @Override + public void setReceiveBufferSize(int size) throws SocketException { + socket.setReceiveBufferSize(size); + } + + @Override + public int getReceiveBufferSize() throws SocketException { + return socket.getReceiveBufferSize(); + } + + @Override + public void setKeepAlive(boolean on) throws SocketException { + socket.setKeepAlive(on); + } + + @Override + public boolean getKeepAlive() throws SocketException { + return socket.getKeepAlive(); + } + + @Override + public void setTrafficClass(int tc) throws SocketException { + socket.setTrafficClass(tc); + } + + @Override + public int getTrafficClass() throws SocketException { + return socket.getTrafficClass(); + } + + @Override + public void setReuseAddress(boolean on) throws SocketException { + socket.setReuseAddress(on); + } + + @Override + public boolean getReuseAddress() throws SocketException { + return socket.getReuseAddress(); + } + + @Override + public void close() throws IOException { + try { + socket.close(); + } finally { + ses.shutdownNow(); + } + } + + @Override + public void shutdownInput() throws IOException { + socket.shutdownInput(); + } + + @Override + public void shutdownOutput() throws IOException { + socket.shutdownOutput(); + } + + @Override + public String toString() { + return socket.toString(); + } + + @Override + public boolean isConnected() { + return socket.isConnected(); + } + + @Override + public boolean isBound() { + return socket.isBound(); + } + + @Override + public boolean isClosed() { + return socket.isClosed(); + } + + @Override + public boolean isInputShutdown() { + return socket.isInputShutdown(); + } + + @Override + public boolean isOutputShutdown() { + return socket.isOutputShutdown(); + } + + /* + * The following three methods were added to java.net.Socket in Java SE 9. + * Since they're not supported on Android, and since we know that we + * never use them in JavaMail, we just stub them out here. + */ + //@Override + public Socket setOption(SocketOption so, T val) throws IOException { + // socket.setOption(so, val); + // return this; + throw new UnsupportedOperationException("WriteTimeoutSocket.setOption"); + } + + //@Override + public T getOption(SocketOption so) throws IOException { + // return socket.getOption(so); + throw new UnsupportedOperationException("WriteTimeoutSocket.getOption"); + } + + //@Override + public Set> supportedOptions() { + // return socket.supportedOptions(); + return Collections.emptySet(); + } + + /** + * KLUDGE for Android, which has this illegal non-Java Compatible method. + * + * @return the FileDescriptor object + */ + public FileDescriptor getFileDescriptor$() { + try { + Method m = Socket.class.getDeclaredMethod("getFileDescriptor$"); + return (FileDescriptor)m.invoke(socket); + } catch (Exception ex) { + return null; + } + } +} + + +/** + * An OutputStream that wraps the Socket's OutputStream and uses + * the ScheduledExecutorService to schedule a task to close the + * socket (aborting the write) if the timeout expires. + */ +class TimeoutOutputStream extends OutputStream { + private final OutputStream os; + private final ScheduledExecutorService ses; + private final Callable timeoutTask; + private final int timeout; + private byte[] b1; + + public TimeoutOutputStream(OutputStream os0, ScheduledExecutorService ses, + int timeout) throws IOException { + this.os = os0; + this.ses = ses; + this.timeout = timeout; + timeoutTask = new Callable() { + @Override + public Object call() throws Exception { + os.close(); // close the stream to abort the write + return null; + } + }; + } + + @Override + public synchronized void write(int b) throws IOException { + if (b1 == null) + b1 = new byte[1]; + b1[0] = (byte)b; + this.write(b1); + } + + @Override + public synchronized void write(byte[] bs, int off, int len) + throws IOException { + if ((off < 0) || (off > bs.length) || (len < 0) || + ((off + len) > bs.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + + // Implement timeout with a scheduled task + ScheduledFuture sf = null; + try { + try { + if (timeout > 0) + sf = ses.schedule(timeoutTask, + timeout, TimeUnit.MILLISECONDS); + } catch (RejectedExecutionException ex) { + // ignore it; Executor was shut down by another thread, + // the following write should fail with IOException + } + os.write(bs, off, len); + } finally { + if (sf != null) + sf.cancel(true); + } + } + + @Override + public void close() throws IOException { + os.close(); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/logging/CollectorFormatter.java b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/CollectorFormatter.java new file mode 100644 index 000000000..6ee2a196e --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/CollectorFormatter.java @@ -0,0 +1,625 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Jason Mehrens. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package com.sun.mail.util.logging; + +import static com.sun.mail.util.logging.LogManagerProperties.fromLogManager; +import java.lang.reflect.UndeclaredThrowableException; +import java.text.MessageFormat; +import java.util.Comparator; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.LogRecord; + +/** + * A LogRecord formatter that takes a sequence of LogRecords and combines them + * into a single summary result. Formating of the head, LogRecord, and tail are + * delegated to the wrapped formatter. + * + *

    + * By default each CollectorFormatter is initialized using the + * following LogManager configuration properties where + * <formatter-name> refers to the fully qualified class name or + * the fully qualified derived class name of the formatter. If properties are + * not defined, or contain invalid values, then the specified default values are + * used. + *

      + *
    • <formatter-name>.comparator name of a + * {@linkplain java.util.Comparator} class used to choose the collected + * LogRecord. If a comparator is specified then the max + * LogRecord is chosen. If comparator is set to the string literal + * null, then the last record is chosen. (defaults to + * {@linkplain SeverityComparator}) + * + *
    • <formatter-name>.comparator.reverse a boolean + * true to collect the min LogRecord or false to + * collect the max LogRecord. (defaults to false) + * + *
    • <formatter-name>.format the + * {@linkplain java.text.MessageFormat MessageFormat} string used to format the + * collected summary statistics. The arguments are explained in detail in the + * {@linkplain #getTail(java.util.logging.Handler) getTail} documentation. + * (defaults to {0}{1}{2}{4,choice,-1#|0#|0<... {4,number,integer} + * more}\n) + * + *
    • <formatter-name>.formatter name of a Formatter class used + * to format the collected LogRecord. (defaults to {@linkplain CompactFormatter}) + * + *
    + * + * @author Jason Mehrens + * @since JavaMail 1.5.2 + */ +public class CollectorFormatter extends Formatter { + /** + * Avoid depending on JMX runtime bean to get the start time. + */ + private static final long INIT_TIME = System.currentTimeMillis(); + /** + * The message format string used as the formatted output. + */ + private final String fmt; + /** + * The formatter used to format the chosen log record. + */ + private final Formatter formatter; + /** + * The comparator used to pick the log record to format. + */ + private final Comparator comparator; + /** + * The last accepted record. Synchronized access is preferred over volatile + * for this class. + */ + private LogRecord last; + /** + * The number of log records that have been formatted. + */ + private long count; + /** + * The number of log produced containing at least one log record. + * Only incremented when this formatter is reset. + */ + private long generation = 1L; + /** + * The number of log records that have been formatted with a thrown object. + */ + private long thrown; + /** + * The eldest log record time or eldest time possible for this instance. + */ + private long minMillis = INIT_TIME; + /** + * The newest log record time. + */ + private long maxMillis = Long.MIN_VALUE; + + /** + * Creates the formatter using the LogManager defaults. + * + * @throws SecurityException if a security manager exists and the caller + * does not have LoggingPermission("control"). + * @throws UndeclaredThrowableException if there are problems loading from + * the LogManager. + */ + public CollectorFormatter() { + final String p = getClass().getName(); + this.fmt = initFormat(p); + this.formatter = initFormatter(p); + this.comparator = initComparator(p); + } + + /** + * Creates the formatter using the given format. + * + * @param format the message format or null to use the LogManager default. + * @throws SecurityException if a security manager exists and the caller + * does not have LoggingPermission("control"). + * @throws UndeclaredThrowableException if there are problems loading from + * the LogManager. + */ + public CollectorFormatter(String format) { + final String p = getClass().getName(); + this.fmt = format == null ? initFormat(p) : format; + this.formatter = initFormatter(p); + this.comparator = initComparator(p); + } + + /** + * Creates the formatter using the given values. + * + * @param format the format string or null to use the LogManager default. + * @param f the formatter used on the collected log record or null to + * specify no formatter. + * @param c the comparator used to determine which log record to format or + * null to specify no comparator. + * @throws SecurityException if a security manager exists and the caller + * does not have LoggingPermission("control"). + * @throws UndeclaredThrowableException if there are problems loading from + * the LogManager. + */ + public CollectorFormatter(String format, Formatter f, + Comparator c) { + final String p = getClass().getName(); + this.fmt = format == null ? initFormat(p) : format; + this.formatter = f; + this.comparator = c; + } + + /** + * Accumulates log records which will be used to produce the final output. + * The output is generated using the {@link #getTail} method which also + * resets this formatter back to its original state. + * + * @param record the record to store. + * @return an empty string. + * @throws NullPointerException if the given record is null. + */ + @Override + public String format(final LogRecord record) { + if (record == null) { + throw new NullPointerException(); + } + + boolean accepted; + do { + final LogRecord peek = peek(); + //The self compare of the first record acts like a type check. + LogRecord update = apply(peek != null ? peek : record, record); + if (peek != update) { //Not identical. + update.getSourceMethodName(); //Infer caller, null check. + accepted = acceptAndUpdate(peek, update); + } else { + accepted = accept(peek, record); + } + } while (!accepted); + return ""; + } + + /** + * Formats the collected LogRecord and summary statistics. The collected + * results are reset after calling this method. The + * {@linkplain java.text.MessageFormat java.text} argument indexes are assigned + * to the following properties: + * + *
      + *
    1. {@code head} the + * {@linkplain Formatter#getHead(java.util.logging.Handler) head} string + * returned from the target formatter and + * {@linkplain #finish(java.lang.String) finished} by this formatter. + *
    2. {@code formatted} the current log record + * {@linkplain Formatter#format(java.util.logging.LogRecord) formatted} by + * the target formatter and {@linkplain #finish(java.lang.String) finished} + * by this formatter. If the formatter is null then record is formatted by + * this {@linkplain #formatMessage(java.util.logging.LogRecord) formatter}. + *
    3. {@code tail} the + * {@linkplain Formatter#getTail(java.util.logging.Handler) tail} string + * returned from the target formatter and + * {@linkplain #finish(java.lang.String) finished} by this formatter. + *
    4. {@code count} the total number of log records + * {@linkplain #format consumed} by this formatter. + *
    5. {@code remaining} the count minus one. + *
    6. {@code thrown} the total number of log records + * {@linkplain #format consumed} by this formatter with an assigned + * {@linkplain java.util.logging.LogRecord#getThrown throwable}. + *
    7. {@code normal messages} the count minus the thrown. + *
    8. {@code minMillis} the eldest log record + * {@linkplain java.util.logging.LogRecord#getMillis event time} + * {@linkplain #format consumed} by this formatter. If the count is zero + * then this is set to the previous max or approximate start time if there + * was no previous max. By default this parameter is defined as a number. + * The format type and format style rules from the + * {@linkplain java.text.MessageFormat} should be used to convert this from + * milliseconds to a date or time. + *
    9. {@code maxMillis} the most recent log record + * {@linkplain java.util.logging.LogRecord#getMillis event time} + * {@linkplain #format consumed} by this formatter. If the count is zero + * then this is set to the {@linkplain System#currentTimeMillis() current time}. + * By default this parameter is defined as a number. The format type and + * format style rules from the {@linkplain java.text.MessageFormat} should be + * used to convert this from milliseconds to a date or time. + *
    10. {@code elapsed} the elapsed time in milliseconds between the + * {@code maxMillis} and {@code minMillis}. + *
    11. {@code startTime} the approximate start time in milliseconds. By + * default this parameter is defined as a number. The format type and format + * style rules from the {@linkplain java.text.MessageFormat} should be used to + * convert this from milliseconds to a date or time. + *
    12. {@code currentTime} the + * {@linkplain System#currentTimeMillis() current time} in milliseconds. By + * default this parameter is defined as a number. The format type and format + * style rules from the {@linkplain java.text.MessageFormat} should be used to + * convert this from milliseconds to a date or time. + *
    13. {@code uptime} the elapsed time in milliseconds between the + * {@code currentTime} and {@code startTime}. + *
    14. {@code generation} the number times this method produced output with + * at least one {@linkplain #format consumed} log record. This can be used + * to track the number of complete reports this formatter has produced. + *
    + * + *

    + * Some example formats:
    + *

      + *
    • {@code com.sun.mail.util.logging.CollectorFormatter.format={0}{1}{2}{4,choice,-1#|0#|0<... {4,number,integer} more}\n} + *

      + * This prints the head ({@code {0}}), format ({@code {1}}), and tail + * ({@code {2}}) from the target formatter followed by the number of + * remaining ({@code {4}}) log records consumed by this formatter if there + * are any remaining records. + *

      +     * Encoding failed.|NullPointerException: null String.getBytes(:913)... 3 more
      +     * 
      + *
    • {@code com.sun.mail.util.logging.CollectorFormatter.format=These {3} messages occurred between\n{7,date,EEE, MMM dd HH:mm:ss:S ZZZ yyyy} and {8,time,EEE, MMM dd HH:mm:ss:S ZZZ yyyy}\n} + *

      + * This prints the count ({@code {3}}) followed by the date and time of the + * eldest log record ({@code {7}}) and the date and time of the most recent + * log record ({@code {8}}). + *

      +     * These 292 messages occurred between
      +     * Tue, Jul 21 14:11:42:449 -0500 2009 and Fri, Nov 20 07:29:24:0 -0600 2009
      +     * 
      + *
    • {@code com.sun.mail.util.logging.CollectorFormatter.format=These {3} messages occurred between {9,choice,86400000#{7,date} {7,time} and {8,time}|86400000<{7,date} and {8,date}}\n} + *

      + * This prints the count ({@code {3}}) and then chooses the format based on + * the elapsed time ({@code {9}}). If the elapsed time is less than one day + * then the eldest log record ({@code {7}}) date and time is formatted + * followed by just the time of the most recent log record ({@code {8}}. + * Otherwise, the just the date of the eldest log record ({@code {7}}) and + * just the date of most recent log record ({@code {8}} is formatted. + *

      +     * These 73 messages occurred between Jul 21, 2009 2:11:42 PM and 2:13:32 PM
      +     *
      +     * These 116 messages occurred between Jul 21, 2009 and Aug 20, 2009
      +     * 
      + *
    • {@code com.sun.mail.util.logging.CollectorFormatter.format={13} alert reports since {10,date}.\n} + *

      + * This prints the generation ({@code {13}}) followed by the start time + * ({@code {10}}) formatted as a date. + *

      +     * 4,320 alert reports since Jul 21, 2012.
      +     * 
      + *
    + * + * @param h the handler or null. + * @return the output string. + */ + @Override + public String getTail(final Handler h) { + super.getTail(h); //Be forward compatible with super.getHead. + return formatRecord(h, true); + } + + /** + * Formats the collected LogRecord and summary statistics. The LogRecord and + * summary statistics are not changed by calling this method. + * + * @return the current record formatted or the default toString. + * @see #getTail(java.util.logging.Handler) + */ + @Override + public String toString() { + String result; + try { + result = formatRecord((Handler) null, false); + } catch (final RuntimeException ignore) { + result = super.toString(); + } + return result; + } + + /** + * Used to choose the collected LogRecord. This implementation returns the + * greater of two LogRecords. + * + * @param t the current record. + * @param u the record that could replace the current. + * @return the greater of the given log records. + * @throws NullPointerException may occur if either record is null. + */ + protected LogRecord apply(final LogRecord t, final LogRecord u) { + if (t == null || u == null) { + throw new NullPointerException(); + } + + if (comparator != null) { + return comparator.compare(t, u) >= 0 ? t : u; + } else { + return u; + } + } + + /** + * Updates the summary statistics only if the expected record matches the + * last record. The update record is not stored. + * + * @param e the LogRecord that is expected. + * @param u the LogRecord used to collect statistics. + * @return true if the last record was the expected record. + * @throws NullPointerException if the update record is null. + */ + private synchronized boolean accept(final LogRecord e, final LogRecord u) { + /** + * LogRecord methods must be called before the check of the last stored + * record to guard against subclasses of LogRecord that might attempt to + * reset the state by triggering a call to getTail. + */ + final long millis = u.getMillis(); //Null check. + final Throwable ex = u.getThrown(); + if (last == e) { //Only if the exact same reference. + if (++count != 1L) { + minMillis = Math.min(minMillis, millis); + } else { //Show single records as instant and not a time period. + minMillis = millis; + } + maxMillis = Math.max(maxMillis, millis); + + if (ex != null) { + ++thrown; + } + return true; + } else { + return false; + } + } + + /** + * Resets all of the collected summary statistics including the LogRecord. + * @param min the current min milliseconds. + */ + private synchronized void reset(final long min) { + if (last != null) { + last = null; + ++generation; + } + + count = 0L; + thrown = 0L; + minMillis = min; + maxMillis = Long.MIN_VALUE; + } + + /** + * Formats the given record with the head and tail. + * + * @param h the Handler or null. + * @param reset true if the summary statistics and LogRecord should be reset + * back to initial values. + * @return the formatted string. + * @see #getTail(java.util.logging.Handler) + */ + private String formatRecord(final Handler h, final boolean reset) { + final LogRecord record; + final long c; + final long t; + final long g; + long msl; + long msh; + long now; + synchronized (this) { + record = last; + c = count; + g = generation; + t = thrown; + msl = minMillis; + msh = maxMillis; + now = System.currentTimeMillis(); + if (c == 0L) { + msh = now; + } + + if (reset) { //BUG ID 6351685 + reset(msh); + } + } + + final String head; + final String msg; + final String tail; + final Formatter f = this.formatter; + if (f != null) { + synchronized (f) { + head = f.getHead(h); + msg = record != null ? f.format(record) : ""; + tail = f.getTail(h); + } + } else { + head = ""; + msg = record != null ? formatMessage(record) : ""; + tail = ""; + } + + Locale l = null; + if (record != null) { + ResourceBundle rb = record.getResourceBundle(); + l = rb == null ? null : rb.getLocale(); + } + + final MessageFormat mf; + if (l == null) { //BUG ID 8039165 + mf = new MessageFormat(fmt); + } else { + mf = new MessageFormat(fmt, l); + } + + /** + * These arguments are described in the getTail documentation. + */ + return mf.format(new Object[]{finish(head), finish(msg), finish(tail), + c, (c - 1L), t, (c - t), msl, msh, (msh - msl), INIT_TIME, now, + (now - INIT_TIME), g}); + } + + /** + * Applied to the head, format, and tail returned by the target formatter. + * This implementation trims all input strings. + * + * @param s the string to transform. + * @return the transformed string. + * @throws NullPointerException if the given string is null. + */ + protected String finish(String s) { + return s.trim(); + } + + /** + * Peek at the current log record. + * + * @return null or the current log record. + */ + private synchronized LogRecord peek() { + return this.last; + } + + /** + * Updates the summary statistics and stores given LogRecord if the expected + * record matches the current record. + * + * @param e the expected record. + * @param u the update record. + * @return true if the update was performed. + * @throws NullPointerException if the update record is null. + */ + private synchronized boolean acceptAndUpdate(LogRecord e, LogRecord u) { + if (accept(e, u)) { + this.last = u; + return true; + } else { + return false; + } + } + + /** + * Gets the message format string from the LogManager or creates the default + * message format string. + * + * @param p the class name prefix. + * @return the format string. + * @throws NullPointerException if the given argument is null. + */ + private String initFormat(final String p) { + String v = fromLogManager(p.concat(".format")); + if (v == null || v.length() == 0) { + v = "{0}{1}{2}{4,choice,-1#|0#|0<... {4,number,integer} more}\n"; + } + return v; + } + + /** + * Gets and creates the formatter from the LogManager or creates the default + * formatter. + * + * @param p the class name prefix. + * @return the formatter. + * @throws NullPointerException if the given argument is null. + * @throws UndeclaredThrowableException if the formatter can not be created. + */ + private Formatter initFormatter(final String p) { + Formatter f; + String v = fromLogManager(p.concat(".formatter")); + if (v != null && v.length() != 0) { + if (!"null".equalsIgnoreCase(v)) { + try { + f = LogManagerProperties.newFormatter(v); + } catch (final RuntimeException re) { + throw re; + } catch (final Exception e) { + throw new UndeclaredThrowableException(e); + } + } else { + f = null; + } + } else { + //Don't force the byte code verifier to load the formatter. + f = Formatter.class.cast(new CompactFormatter()); + } + return f; + } + + /** + * Gets and creates the comparator from the LogManager or returns the + * default comparator. + * + * @param p the class name prefix. + * @return the comparator or null. + * @throws IllegalArgumentException if it was specified that the comparator + * should be reversed but no initial comparator was specified. + * @throws NullPointerException if the given argument is null. + * @throws UndeclaredThrowableException if the comparator can not be + * created. + */ + @SuppressWarnings("unchecked") + private Comparator initComparator(final String p) { + Comparator c; + final String name = fromLogManager(p.concat(".comparator")); + final String reverse = fromLogManager(p.concat(".comparator.reverse")); + try { + if (name != null && name.length() != 0) { + if (!"null".equalsIgnoreCase(name)) { + c = LogManagerProperties.newComparator(name); + if (Boolean.parseBoolean(reverse)) { + assert c != null; + c = LogManagerProperties.reverseOrder(c); + } + } else { + if (reverse != null) { + throw new IllegalArgumentException( + "No comparator to reverse."); + } else { + c = null; //No ordering. + } + } + } else { + if (reverse != null) { + throw new IllegalArgumentException( + "No comparator to reverse."); + } else { + //Don't force the byte code verifier to load the comparator. + c = Comparator.class.cast(SeverityComparator.getInstance()); + } + } + } catch (final RuntimeException re) { + throw re; //Avoid catch all. + } catch (final Exception e) { + throw new UndeclaredThrowableException(e); + } + return c; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/logging/CompactFormatter.java b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/CompactFormatter.java new file mode 100644 index 000000000..73ea85d8f --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/CompactFormatter.java @@ -0,0 +1,889 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Jason Mehrens. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package com.sun.mail.util.logging; + +import java.util.*; +import java.util.logging.LogRecord; + +/** + * A plain text formatter that can produce fixed width output. By default this + * formatter will produce output no greater than 160 characters wide plus the + * separator and newline characters. Only specified fields support an + * {@linkplain #toAlternate(java.lang.String) alternate} fixed width format. + *

    + * By default each CompactFormatter is initialized using the following + * LogManager configuration properties where + * <formatter-name> refers to the fully qualified class name or + * the fully qualified derived class name of the formatter. If properties are + * not defined, or contain invalid values, then the specified default values are + * used. + *

      + *
    • <formatter-name>.format - the {@linkplain java.util.Formatter + * format} string used to transform the output. The format string can be + * used to fix the output size. (defaults to %7$#.160s%n)
    • + *
    + * + * @author Jason Mehrens + * @since JavaMail 1.5.2 + */ +public class CompactFormatter extends java.util.logging.Formatter { + + /** + * Load any declared classes to workaround GLASSFISH-21258. + */ + static { + loadDeclaredClasses(); + } + + /** + * Used to load declared classes encase class loader doesn't allow loading + * during JVM termination. This method is used with unit testing. + * + * @return an array of classes never null. + */ + private static Class[] loadDeclaredClasses() { + return new Class[]{Alternate.class}; + } + + /** + * Holds the java.util.Formatter pattern. + */ + private final String fmt; + + /** + * Creates an instance with a default format pattern. + */ + public CompactFormatter() { + String p = getClass().getName(); + this.fmt = initFormat(p); + } + + /** + * Creates an instance with the given format pattern. + * + * @param format the {@linkplain java.util.Formatter pattern} or null to use + * the LogManager default. The arguments are described in the + * {@linkplain #format(java.util.logging.LogRecord) format} method. + */ + public CompactFormatter(final String format) { + String p = getClass().getName(); + this.fmt = format == null ? initFormat(p) : format; + } + + /** + * Format the given log record and returns the formatted string. The + * {@linkplain java.util.Formatter#format(java.lang.String, java.lang.Object...) + * java.util} argument indexes are assigned to the following properties: + * + *
      + *
    1. {@code format} - the {@linkplain java.util.Formatter + * java.util.Formatter} format string specified in the + * <formatter-name>.format property or the format that was given when + * this formatter was created.
    2. + *
    3. {@code date} - if the log record supports nanoseconds then a + * ZonedDateTime object representing the event time of the log record in the + * system time zone. Otherwise, a {@linkplain Date} object representing + * {@linkplain LogRecord#getMillis event time} of the log record.
    4. + *
    5. {@code source} - a string representing the caller, if available; + * otherwise, the logger's name.
    6. + *
    7. {@code logger} - the logger's + * {@linkplain Class#getSimpleName() simple} + * {@linkplain LogRecord#getLoggerName() name}.
    8. + *
    9. {@code level} - the + * {@linkplain java.util.logging.Level#getLocalizedName log level}.
    10. + *
    11. {@code message} - the formatted log message returned from the + * {@linkplain #formatMessage(LogRecord)} method.
    12. + *
    13. {@code thrown} - a string representing the + * {@linkplain LogRecord#getThrown throwable} associated with the log record + * and a relevant stack trace element if available; otherwise, an empty + * string is used.
    14. + *
    15. {@code message|thrown} The message and the thrown properties joined + * as one parameter. This parameter supports + * {@linkplain #toAlternate(java.lang.String) alternate} form.
    16. + *
    17. {@code thrown|message} The thrown and message properties joined as + * one parameter. This parameter supports + * {@linkplain #toAlternate(java.lang.String) alternate} form.
    18. + *
    19. {@code sequence} the + * {@linkplain LogRecord#getSequenceNumber() sequence number} if the given + * log record.
    20. + *
    21. {@code thread id} the {@linkplain LogRecord#getThreadID() thread id} + * of the given log record. By default this is formatted as a {@code long} + * by an unsigned conversion.
    22. + *
    23. {@code error} the throwable + * {@linkplain Class#getSimpleName() simple class name} and + * {@linkplain #formatError(LogRecord) error message} without any stack + * trace.
    24. + *
    25. {@code message|error} The message and error properties joined as one + * parameter. This parameter supports + * {@linkplain #toAlternate(java.lang.String) alternate} form.
    26. + *
    27. {@code error|message} The error and message properties joined as one + * parameter. This parameter supports + * {@linkplain #toAlternate(java.lang.String) alternate} form.
    28. + *
    29. {@code backtrace} only the + * {@linkplain #formatBackTrace(LogRecord) stack trace} of the given + * throwable.
    30. + *
    31. {@code bundlename} the resource bundle + * {@linkplain LogRecord#getResourceBundleName() name} of the given log + * record.
    32. + *
    33. {@code key} the {@linkplain LogRecord#getMessage() raw message} + * before localization or formatting.
    34. + *
    + * + *

    + * Some example formats:
    + *

      + *
    • {@code com.sun.mail.util.logging.CompactFormatter.format=%7$#.160s%n} + *

      + * This prints only 160 characters of the message|thrown ({@code 7$}) using + * the {@linkplain #toAlternate(java.lang.String) alternate} form. The + * separator is not included as part of the total width. + *

      +     * Encoding failed.|NullPointerException: null String.getBytes(:913)
      +     * 
      + * + *
    • {@code com.sun.mail.util.logging.CompactFormatter.format=%7$#.20s%n} + *

      + * This prints only 20 characters of the message|thrown ({@code 7$}) using + * the {@linkplain #toAlternate(java.lang.String) alternate} form. This will + * perform a weighted truncation of both the message and thrown properties + * of the log record. The separator is not included as part of the total + * width. + *

      +     * Encoding|NullPointerE
      +     * 
      + * + *
    • {@code com.sun.mail.util.logging.CompactFormatter.format=%1$tc %2$s%n%4$s: %5$s%6$s%n} + *

      + * This prints the timestamp ({@code 1$}) and the source ({@code 2$}) on the + * first line. The second line is the log level ({@code 4$}), log message + * ({@code 5$}), and the throwable with a relevant stack trace element + * ({@code 6$}) if one is available. + *

      +     * Fri Nov 20 07:29:24 CST 2009 MyClass fatal
      +     * SEVERE: Encoding failed.NullPointerException: null String.getBytes(:913)
      +     * 
      + * + *
    • {@code com.sun.mail.util.logging.CompactFormatter.format=%4$s: %12$#.160s%n} + *

      + * This prints the log level ({@code 4$}) and only 160 characters of the + * message|error ({@code 12$}) using the alternate form. + *

      +     * SEVERE: Unable to send notification.|SocketException: Permission denied: connect
      +     * 
      + * + *
    • {@code com.sun.mail.util.logging.CompactFormatter.format=[%9$d][%1$tT][%10$d][%2$s] %5$s%n%6$s%n} + *

      + * This prints the sequence ({@code 9$}), event time ({@code 1$}) as 24 hour + * time, thread id ({@code 10$}), source ({@code 2$}), log message + * ({@code 5$}), and the throwable with back trace ({@code 6$}). + *

      +     * [125][14:11:42][38][MyClass fatal] Unable to send notification.
      +     * SocketException: Permission denied: connect SMTPTransport.openServer(:1949)
      +     * 
      + * + *
    + * + * @param record the log record to format. + * @return the formatted record. + * @throws NullPointerException if the given record is null. + */ + @Override + public String format(final LogRecord record) { + //LogRecord is mutable so define local vars. + ResourceBundle rb = record.getResourceBundle(); + Locale l = rb == null ? null : rb.getLocale(); + + String msg = formatMessage(record); + String thrown = formatThrown(record); + String err = formatError(record); + Object[] params = { + formatZonedDateTime(record), + formatSource(record), + formatLoggerName(record), + formatLevel(record), + msg, + thrown, + new Alternate(msg, thrown), + new Alternate(thrown, msg), + record.getSequenceNumber(), + formatThreadID(record), + err, + new Alternate(msg, err), + new Alternate(err, msg), + formatBackTrace(record), + record.getResourceBundleName(), + record.getMessage()}; + + if (l == null) { //BUG ID 6282094 + return String.format(fmt, params); + } else { + return String.format(l, fmt, params); + } + } + + /** + * Formats message for the log record. This method removes any fully + * qualified throwable class names from the message. + * + * @param record the log record. + * @return the formatted message string. + */ + @Override + public String formatMessage(final LogRecord record) { + String msg = super.formatMessage(record); + msg = replaceClassName(msg, record.getThrown()); + msg = replaceClassName(msg, record.getParameters()); + return msg; + } + + /** + * Formats the message from the thrown property of the log record. This + * method replaces fully qualified throwable class names from the message + * cause chain with simple class names. + * + * @param t the throwable to format or null. + * @return the empty string if null was given or the formatted message + * string from the throwable which may be null. + */ + public String formatMessage(final Throwable t) { + String r; + if (t != null) { + final Throwable apply = apply(t); + final String m = apply.getLocalizedMessage(); + final String s = apply.toString(); + final String sn = simpleClassName(apply.getClass()); + if (!isNullOrSpaces(m)) { + if (s.contains(m)) { + if (s.startsWith(apply.getClass().getName()) + || s.startsWith(sn)) { + r = replaceClassName(m, t); + } else { + r = replaceClassName(simpleClassName(s), t); + } + } else { + r = replaceClassName(simpleClassName(s) + ": " + m, t); + } + } else { + r = replaceClassName(simpleClassName(s), t); + } + + if (!r.contains(sn)) { + r = sn + ": " + r; + } + } else { + r = ""; + } + return r; + } + + /** + * Formats the level property of the given log record. + * + * @param record the record. + * @return the formatted logger name. + * @throws NullPointerException if the given record is null. + */ + public String formatLevel(final LogRecord record) { + return record.getLevel().getLocalizedName(); + } + + /** + * Formats the source from the given log record. + * + * @param record the record. + * @return the formatted source of the log record. + * @throws NullPointerException if the given record is null. + */ + public String formatSource(final LogRecord record) { + String source = record.getSourceClassName(); + if (source != null) { + if (record.getSourceMethodName() != null) { + source = simpleClassName(source) + " " + + record.getSourceMethodName(); + } else { + source = simpleClassName(source); + } + } else { + source = simpleClassName(record.getLoggerName()); + } + return source; + } + + /** + * Formats the logger name property of the given log record. + * + * @param record the record. + * @return the formatted logger name. + * @throws NullPointerException if the given record is null. + */ + public String formatLoggerName(final LogRecord record) { + return simpleClassName(record.getLoggerName()); + } + + /** + * Formats the thread id property of the given log record. By default this + * is formatted as a {@code long} by an unsigned conversion. + * + * @param record the record. + * @return the formatted thread id as a number. + * @throws NullPointerException if the given record is null. + * @since JavaMail 1.5.4 + */ + public Number formatThreadID(final LogRecord record) { + /** + * Thread.getID is defined as long and LogRecord.getThreadID is defined + * as int. Convert to unsigned as a means to better map the two types of + * thread identifiers. + */ + return (((long) record.getThreadID()) & 0xffffffffL); + } + + /** + * Formats the thrown property of a LogRecord. The returned string will + * contain a throwable message with a back trace. + * + * @param record the record. + * @return empty string if nothing was thrown or formatted string. + * @throws NullPointerException if the given record is null. + * @see #apply(java.lang.Throwable) + * @see #formatBackTrace(java.util.logging.LogRecord) + */ + public String formatThrown(final LogRecord record) { + String msg; + final Throwable t = record.getThrown(); + if (t != null) { + String site = formatBackTrace(record); + msg = formatMessage(t) + (isNullOrSpaces(site) ? "" : ' ' + site); + } else { + msg = ""; + } + return msg; + } + + /** + * Formats the thrown property of a LogRecord as an error message. The + * returned string will not contain a back trace. + * + * @param record the record. + * @return empty string if nothing was thrown or formatted string. + * @throws NullPointerException if the given record is null. + * @see Throwable#toString() + * @see #apply(java.lang.Throwable) + * @see #formatMessage(java.lang.Throwable) + * @since JavaMail 1.5.4 + */ + public String formatError(final LogRecord record) { + return formatMessage(record.getThrown()); + } + + /** + * Formats the back trace for the given log record. + * + * @param record the log record to format. + * @return the formatted back trace. + * @throws NullPointerException if the given record is null. + * @see #apply(java.lang.Throwable) + * @see #formatThrown(java.util.logging.LogRecord) + * @see #ignore(java.lang.StackTraceElement) + */ + public String formatBackTrace(final LogRecord record) { + String site = ""; + final Throwable t = record.getThrown(); + if (t != null) { + final Throwable root = apply(t); + StackTraceElement[] trace = root.getStackTrace(); + site = findAndFormat(trace); + if (isNullOrSpaces(site)) { + int limit = 0; + for (Throwable c = t; c != null; c = c.getCause()) { + StackTraceElement[] ste = c.getStackTrace(); + site = findAndFormat(ste); + if (!isNullOrSpaces(site)) { + break; + } else { + if (trace.length == 0) { + trace = ste; + } + } + + //Deal with excessive cause chains + //and cyclic throwables. + if (++limit == (1 << 16)) { + break; //Give up. + } + } + + //Punt. + if (isNullOrSpaces(site) && trace.length != 0) { + site = formatStackTraceElement(trace[0]); + } + } + } + return site; + } + + /** + * Finds and formats the first stack frame of interest. + * + * @param trace the fill stack to examine. + * @return a String that best describes the call site. + * @throws NullPointerException if stack trace element array is null. + */ + private String findAndFormat(final StackTraceElement[] trace) { + String site = ""; + for (StackTraceElement s : trace) { + if (!ignore(s)) { + site = formatStackTraceElement(s); + break; + } + } + + //Check if all code was compiled with no debugging info. + if (isNullOrSpaces(site)) { + for (StackTraceElement s : trace) { + if (!defaultIgnore(s)) { + site = formatStackTraceElement(s); + break; + } + } + } + return site; + } + + /** + * Formats a stack trace element into a simple call site. + * + * @param s the stack trace element to format. + * @return the formatted stack trace element. + * @throws NullPointerException if stack trace element is null. + * @see #formatThrown(java.util.logging.LogRecord) + */ + private String formatStackTraceElement(final StackTraceElement s) { + String v = simpleClassName(s.getClassName()); + String result; + if (v != null) { + result = s.toString().replace(s.getClassName(), v); + } else { + result = s.toString(); + } + + //If the class name contains the simple file name then remove file name. + v = simpleFileName(s.getFileName()); + if (v != null && result.startsWith(v)) { + result = result.replace(s.getFileName(), ""); + } + return result; + } + + /** + * Chooses a single throwable from the cause chain that will be formatted. + * This implementation chooses the throwable that best describes the chain. + * Subclasses can override this method to choose an alternate throwable for + * formatting. + * + * @param t the throwable from the log record. + * @return the chosen throwable or null only if the given argument is null. + * @see #formatThrown(java.util.logging.LogRecord) + */ + protected Throwable apply(final Throwable t) { + return SeverityComparator.getInstance().apply(t); + } + + /** + * Determines if a stack frame should be ignored as the cause of an error. + * + * @param s the stack trace element. + * @return true if this frame should be ignored. + * @see #formatThrown(java.util.logging.LogRecord) + */ + protected boolean ignore(final StackTraceElement s) { + return isUnknown(s) || defaultIgnore(s); + } + + /** + * Defines the alternate format. This implementation removes all control + * characters from the given string. + * + * @param s any string or null. + * @return null if the argument was null otherwise, an alternate string. + */ + protected String toAlternate(final String s) { + return s != null ? s.replaceAll("[\\x00-\\x1F\\x7F]+", "") : null; + } + + /** + * Gets the zoned date time from the given log record. + * + * @param record the current log record. + * @return a zoned date time or a legacy date object. + * @throws NullPointerException if the given record is null. + * @since JavaMail 1.5.6 + */ + private Comparable formatZonedDateTime(final LogRecord record) { + Comparable zdt = LogManagerProperties.getZonedDateTime(record); + if (zdt == null) { + zdt = new java.util.Date(record.getMillis()); + } + return zdt; + } + + /** + * Determines if a stack frame should be ignored as the cause of an error. + * This does not check for unknown line numbers because code can be compiled + * without debugging info. + * + * @param s the stack trace element. + * @return true if this frame should be ignored. + */ + private boolean defaultIgnore(final StackTraceElement s) { + return isSynthetic(s) || isStaticUtility(s) || isReflection(s); + } + + /** + * Determines if a stack frame is for a static utility class. + * + * @param s the stack trace element. + * @return true if this frame should be ignored. + */ + private boolean isStaticUtility(final StackTraceElement s) { + try { + return LogManagerProperties.isStaticUtilityClass(s.getClassName()); + } catch (RuntimeException ignore) { + } catch (Exception | LinkageError ignore) { + } + final String cn = s.getClassName(); + return (cn.endsWith("s") && !cn.endsWith("es")) + || cn.contains("Util") || cn.endsWith("Throwables"); + } + + /** + * Determines if a stack trace element is for a synthetic method. + * + * @param s the stack trace element. + * @return true if synthetic. + * @throws NullPointerException if stack trace element is null. + */ + private boolean isSynthetic(final StackTraceElement s) { + return s.getMethodName().indexOf('$') > -1; + } + + /** + * Determines if a stack trace element has an unknown line number or a + * native line number. + * + * @param s the stack trace element. + * @return true if the line number is unknown. + * @throws NullPointerException if stack trace element is null. + */ + private boolean isUnknown(final StackTraceElement s) { + return s.getLineNumber() < 0; + } + + /** + * Determines if a stack trace element represents a reflection frame. + * + * @param s the stack trace element. + * @return true if the line number is unknown. + * @throws NullPointerException if stack trace element is null. + */ + private boolean isReflection(final StackTraceElement s) { + try { + return LogManagerProperties.isReflectionClass(s.getClassName()); + } catch (RuntimeException ignore) { + } catch (Exception | LinkageError ignore) { + } + return s.getClassName().startsWith("java.lang.reflect.") + || s.getClassName().startsWith("sun.reflect."); + } + + /** + * Creates the format pattern for this formatter. + * + * @param p the class name prefix. + * @return the java.util.Formatter format string. + * @throws NullPointerException if the given class name is null. + */ + private String initFormat(final String p) { + String v = LogManagerProperties.fromLogManager(p.concat(".format")); + if (isNullOrSpaces(v)) { + v = "%7$#.160s%n"; //160 chars split between message and thrown. + } + return v; + } + + /** + * Searches the given message for all instances fully qualified class name + * with simple class name based off of the types contained in the given + * parameter array. + * + * @param msg the message. + * @param t the throwable cause chain to search or null. + * @return the modified message string. + */ + private static String replaceClassName(String msg, Throwable t) { + if (!isNullOrSpaces(msg)) { + int limit = 0; + for (Throwable c = t; c != null; c = c.getCause()) { + final Class k = c.getClass(); + msg = msg.replace(k.getName(), simpleClassName(k)); + + //Deal with excessive cause chains and cyclic throwables. + if (++limit == (1 << 16)) { + break; //Give up. + } + } + } + return msg; + } + + /** + * Searches the given message for all instances fully qualified class name + * with simple class name based off of the types contained in the given + * parameter array. + * + * @param msg the message or null. + * @param p the parameter array or null. + * @return the modified message string. + */ + private static String replaceClassName(String msg, Object[] p) { + if (!isNullOrSpaces(msg) && p != null) { + for (Object o : p) { + if (o != null) { + final Class k = o.getClass(); + msg = msg.replace(k.getName(), simpleClassName(k)); + } + } + } + return msg; + } + + /** + * Gets the simple class name from the given class. This is a workaround for + * BUG ID JDK-8057919. + * + * @param k the class object. + * @return the simple class name or null. + * @since JavaMail 1.5.3 + */ + private static String simpleClassName(final Class k) { + try { + return k.getSimpleName(); + } catch (final InternalError JDK8057919) { + } + return simpleClassName(k.getName()); + } + + /** + * Converts a fully qualified class name to a simple class name. If the + * leading part of the given string is not a legal class name then the given + * string is returned. + * + * @param name the fully qualified class name prefix or null. + * @return the simple class name or given input. + */ + private static String simpleClassName(String name) { + if (name != null) { + int cursor = 0; + int sign = -1; + int dot = -1; + for (int c, prev = dot; cursor < name.length(); + cursor += Character.charCount(c)) { + c = name.codePointAt(cursor); + if (!Character.isJavaIdentifierPart(c)) { + if (c == ((int) '.')) { + if ((dot + 1) != cursor && (dot + 1) != sign) { + prev = dot; + dot = cursor; + } else { + return name; + } + } else { + if ((dot + 1) == cursor) { + dot = prev; + } + break; + } + } else { + if (c == ((int) '$')) { + sign = cursor; + } + } + } + + if (dot > -1 && ++dot < cursor && ++sign < cursor) { + name = name.substring(sign > dot ? sign : dot); + } + } + return name; + } + + /** + * Converts a file name with an extension to a file name without an + * extension. + * + * @param name the full file name or null. + * @return the simple file name or null. + */ + private static String simpleFileName(String name) { + if (name != null) { + final int index = name.lastIndexOf('.'); + name = index > -1 ? name.substring(0, index) : name; + } + return name; + } + + /** + * Determines is the given string is null or spaces. + * + * @param s the string or null. + * @return true if null or spaces. + */ + private static boolean isNullOrSpaces(final String s) { + return s == null || s.trim().length() == 0; + } + + /** + * Used to format two arguments as fixed length message. + */ + private class Alternate implements java.util.Formattable { + + /** + * The left side of the output. + */ + private final String left; + /** + * The right side of the output. + */ + private final String right; + + /** + * Creates an alternate output. + * + * @param left the left side or null. + * @param right the right side or null. + */ + Alternate(final String left, final String right) { + this.left = String.valueOf(left); + this.right = String.valueOf(right); + } + + @SuppressWarnings("override") //JDK-6954234 + public void formatTo(java.util.Formatter formatter, int flags, + int width, int precision) { + + String l = left; + String r = right; + if ((flags & java.util.FormattableFlags.UPPERCASE) + == java.util.FormattableFlags.UPPERCASE) { + l = l.toUpperCase(formatter.locale()); + r = r.toUpperCase(formatter.locale()); + } + + if ((flags & java.util.FormattableFlags.ALTERNATE) + == java.util.FormattableFlags.ALTERNATE) { + l = toAlternate(l); + r = toAlternate(r); + } + + if (precision <= 0) { + precision = Integer.MAX_VALUE; + } + + int fence = Math.min(l.length(), precision); + if (fence > (precision >> 1)) { + fence = Math.max(fence - r.length(), fence >> 1); + } + + if (fence > 0) { + if (fence > l.length() + && Character.isHighSurrogate(l.charAt(fence - 1))) { + --fence; + } + l = l.substring(0, fence); + } + r = r.substring(0, Math.min(precision - fence, r.length())); + + if (width > 0) { + final int half = width >> 1; + if (l.length() < half) { + l = pad(flags, l, half); + } + + if (r.length() < half) { + r = pad(flags, r, half); + } + } + + Object[] empty = Collections.emptySet().toArray(); + formatter.format(l, empty); + if (l.length() != 0 && r.length() != 0) { + formatter.format("|", empty); + } + formatter.format(r, empty); + } + + /** + * Pad the given input string. + * + * @param flags the formatter flags. + * @param s the string to pad. + * @param length the final string length. + * @return the padded string. + */ + private String pad(int flags, String s, int length) { + final int padding = length - s.length(); + final StringBuilder b = new StringBuilder(length); + if ((flags & java.util.FormattableFlags.LEFT_JUSTIFY) + == java.util.FormattableFlags.LEFT_JUSTIFY) { + for (int i = 0; i < padding; ++i) { + b.append('\u0020'); + } + b.append(s); + } else { + b.append(s); + for (int i = 0; i < padding; ++i) { + b.append('\u0020'); + } + } + return b.toString(); + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/logging/DurationFilter.java b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/DurationFilter.java new file mode 100644 index 000000000..9ecefd8c9 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/DurationFilter.java @@ -0,0 +1,468 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2015-2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015-2017 Jason Mehrens. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package com.sun.mail.util.logging; + +import static com.sun.mail.util.logging.LogManagerProperties.fromLogManager; +import java.util.logging.*; + +/** + * A filter used to limit log records based on a maximum generation rate. + * + * The duration specified is used to compute the record rate and the amount of + * time the filter will reject records once the rate has been exceeded. Once the + * rate is exceeded records are not allowed until the duration has elapsed. + * + *

    + * By default each {@code DurationFilter} is initialized using the following + * LogManager configuration properties where {@code } refers to the + * fully qualified class name of the handler. If properties are not defined, or + * contain invalid values, then the specified default values are used. + * + *

      + *
    • {@literal }.records the max number of records per duration. + * A numeric long integer or a multiplication expression can be used as the + * value. (defaults to {@code 1000}) + * + *
    • {@literal }.duration the number of milliseconds to suppress + * log records from being published. This is also used as duration to determine + * the log record rate. A numeric long integer or a multiplication expression + * can be used as the value. If the {@code java.time} package is available then + * an ISO-8601 duration format of {@code PnDTnHnMn.nS} can be used as the value. + * The suffixes of "D", "H", "M" and "S" are for days, hours, minutes and + * seconds. The suffixes must occur in order. The seconds can be specified with + * a fractional component to declare milliseconds. (defaults to {@code PT15M}) + *
    + * + *

    + * For example, the settings to limit {@code MailHandler} with a default + * capacity to only send a maximum of two email messages every six minutes would + * be as follows: + *

    + * {@code
    + *  com.sun.mail.util.logging.MailHandler.filter = com.sun.mail.util.logging.DurationFilter
    + *  com.sun.mail.util.logging.MailHandler.capacity = 1000
    + *  com.sun.mail.util.logging.DurationFilter.records = 2L * 1000L
    + *  com.sun.mail.util.logging.DurationFilter.duration = PT6M
    + * }
    + * 
    + * + * + * @author Jason Mehrens + * @since JavaMail 1.5.5 + */ +public class DurationFilter implements Filter { + + /** + * The number of expected records per duration. + */ + private final long records; + /** + * The duration in milliseconds used to determine the rate. The duration is + * also used as the amount of time that the filter will not allow records + * when saturated. + */ + private final long duration; + /** + * The number of records seen for the current duration. This value negative + * if saturated. Zero is considered saturated but is reserved for recording + * the first duration. + */ + private long count; + /** + * The most recent record time seen for the current duration. + */ + private long peak; + /** + * The start time for the current duration. + */ + private long start; + + /** + * Creates the filter using the default properties. + */ + public DurationFilter() { + this.records = checkRecords(initLong(".records")); + this.duration = checkDuration(initLong(".duration")); + } + + /** + * Creates the filter using the given properties. Default values are used if + * any of the given values are outside the allowed range. + * + * @param records the number of records per duration. + * @param duration the number of milliseconds to suppress log records from + * being published. + */ + public DurationFilter(final long records, final long duration) { + this.records = checkRecords(records); + this.duration = checkDuration(duration); + } + + /** + * Determines if this filter is equal to another filter. + * + * @param obj the given object. + * @return true if equal otherwise false. + */ + @Override + public boolean equals(final Object obj) { + if (this == obj) { //Avoid locks and deal with rapid state changes. + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + final DurationFilter other = (DurationFilter) obj; + if (this.records != other.records) { + return false; + } + + if (this.duration != other.duration) { + return false; + } + + final long c; + final long p; + final long s; + synchronized (this) { + c = this.count; + p = this.peak; + s = this.start; + } + + synchronized (other) { + if (c != other.count || p != other.peak || s != other.start) { + return false; + } + } + return true; + } + + /** + * Determines if this filter is able to accept the maximum number of log + * records for this instant in time. The result is a best-effort estimate + * and should be considered out of date as soon as it is produced. This + * method is designed for use in monitoring the state of this filter. + * + * @return true if the filter is idle; false otherwise. + */ + public boolean isIdle() { + return test(0L, System.currentTimeMillis()); + } + + /** + * Returns a hash code value for this filter. + * + * @return hash code for this filter. + */ + @Override + public int hashCode() { + int hash = 3; + hash = 89 * hash + (int) (this.records ^ (this.records >>> 32)); + hash = 89 * hash + (int) (this.duration ^ (this.duration >>> 32)); + return hash; + } + + /** + * Check if the given log record should be published. This method will + * modify the internal state of this filter. + * + * @param record the log record to check. + * @return true if allowed; false otherwise. + * @throws NullPointerException if given record is null. + */ + @SuppressWarnings("override") //JDK-6954234 + public boolean isLoggable(final LogRecord record) { + return accept(record.getMillis()); + } + + /** + * Determines if this filter will accept log records for this instant in + * time. The result is a best-effort estimate and should be considered out + * of date as soon as it is produced. This method is designed for use in + * monitoring the state of this filter. + * + * @return true if the filter is not saturated; false otherwise. + */ + public boolean isLoggable() { + return test(records, System.currentTimeMillis()); + } + + /** + * Returns a string representation of this filter. The result is a + * best-effort estimate and should be considered out of date as soon as it + * is produced. + * + * @return a string representation of this filter. + */ + @Override + public String toString() { + boolean idle; + boolean loggable; + synchronized (this) { + final long millis = System.currentTimeMillis(); + idle = test(0L, millis); + loggable = test(records, millis); + } + + return getClass().getName() + "{records=" + records + + ", duration=" + duration + + ", idle=" + idle + + ", loggable=" + loggable + '}'; + } + + /** + * Creates a copy of this filter that retains the filter settings but does + * not include the current filter state. The newly create clone acts as if + * it has never seen any records. + * + * @return a copy of this filter. + * @throws CloneNotSupportedException if this filter is not allowed to be + * cloned. + */ + @Override + protected DurationFilter clone() throws CloneNotSupportedException { + final DurationFilter clone = (DurationFilter) super.clone(); + clone.count = 0L; //Reset the filter state. + clone.peak = 0L; + clone.start = 0L; + return clone; + } + + /** + * Checks if this filter is not saturated or bellow a maximum rate. + * + * @param limit the number of records allowed to be under the rate. + * @param millis the current time in milliseconds. + * @return true if not saturated or bellow the rate. + */ + private boolean test(final long limit, final long millis) { + assert limit >= 0L : limit; + final long c; + final long s; + synchronized (this) { + c = count; + s = start; + } + + if (c > 0L) { //If not saturated. + if ((millis - s) >= duration || c < limit) { + return true; + } + } else { //Subtraction is used to deal with numeric overflow. + if ((millis - s) >= 0L || c == 0L) { + return true; + } + } + return false; + } + + /** + * Determines if the record is loggable by time. + * + * @param millis the log record milliseconds. + * @return true if accepted false otherwise. + */ + private synchronized boolean accept(final long millis) { + //Subtraction is used to deal with numeric overflow of millis. + boolean allow; + if (count > 0L) { //If not saturated. + if ((millis - peak) > 0L) { + peak = millis; //Record the new peak. + } + + //Under the rate if the count has not been reached. + if (count != records) { + ++count; + allow = true; + } else { + if ((peak - start) >= duration) { + count = 1L; //Start a new duration. + start = peak; + allow = true; + } else { + count = -1L; //Saturate for the duration. + start = peak + duration; + allow = false; + } + } + } else { + //If the saturation period has expired or this is the first record + //then start a new duration and allow records. + if ((millis - start) >= 0L || count == 0L) { + count = 1L; + start = millis; + peak = millis; + allow = true; + } else { + allow = false; //Remain in a saturated state. + } + } + return allow; + } + + /** + * Reads a long value or multiplication expression from the LogManager. If + * the value can not be parsed or is not defined then Long.MIN_VALUE is + * returned. + * + * @param suffix a dot character followed by the key name. + * @return a long value or Long.MIN_VALUE if unable to parse or undefined. + * @throws NullPointerException if suffix is null. + */ + private long initLong(final String suffix) { + long result = 0L; + final String p = getClass().getName(); + String value = fromLogManager(p.concat(suffix)); + if (value != null && value.length() != 0) { + value = value.trim(); + if (isTimeEntry(suffix, value)) { + try { + result = LogManagerProperties.parseDurationToMillis(value); + } catch (final RuntimeException ignore) { + } catch (final Exception ignore) { + } catch (final LinkageError ignore) { + } + } + + if (result == 0L) { //Zero is invalid. + try { + result = 1L; + for (String s : tokenizeLongs(value)) { + if (s.endsWith("L") || s.endsWith("l")) { + s = s.substring(0, s.length() - 1); + } + result = multiplyExact(result, Long.parseLong(s)); + } + } catch (final RuntimeException ignore) { + result = Long.MIN_VALUE; + } + } + } else { + result = Long.MIN_VALUE; + } + return result; + } + + /** + * Determines if the given suffix can be a time unit and the value is + * encoded as an ISO ISO-8601 duration format. + * + * @param suffix the suffix property. + * @param value the value of the property. + * @return true if the entry is a time entry. + * @throws IndexOutOfBoundsException if value is empty. + * @throws NullPointerException if either argument is null. + */ + private boolean isTimeEntry(final String suffix, final String value) { + return (value.charAt(0) == 'P' || value.charAt(0) == 'p') + && suffix.equals(".duration"); + } + + /** + * Parse any long value or multiplication expressions into tokens. + * + * @param value the expression or value. + * @return an array of long tokens, never empty. + * @throws NullPointerException if the given value is null. + * @throws NumberFormatException if the expression is invalid. + */ + private static String[] tokenizeLongs(final String value) { + String[] e; + final int i = value.indexOf('*'); + if (i > -1 && (e = value.split("\\s*\\*\\s*")).length != 0) { + if (i == 0 || value.charAt(value.length() - 1) == '*') { + throw new NumberFormatException(value); + } + + if (e.length == 1) { + throw new NumberFormatException(e[0]); + } + } else { + e = new String[]{value}; + } + return e; + } + + /** + * Multiply and check for overflow. This can be replaced with + * {@code java.lang.Math.multiplyExact} when JavaMail requires JDK 8. + * + * @param x the first value. + * @param y the second value. + * @return x times y. + * @throws ArithmeticException if overflow is detected. + */ + private static long multiplyExact(final long x, final long y) { + long r = x * y; + if (((Math.abs(x) | Math.abs(y)) >>> 31L != 0L)) { + if (((y != 0L) && (r / y != x)) + || (x == Long.MIN_VALUE && y == -1L)) { + throw new ArithmeticException(); + } + } + return r; + } + + /** + * Converts record count to a valid record count. If the value is out of + * bounds then the default record count is used. + * + * @param records the record count. + * @return a valid number of record count. + */ + private static long checkRecords(final long records) { + return records > 0L ? records : 1000L; + } + + /** + * Converts the duration to a valid duration. If the value is out of bounds + * then the default duration is used. + * + * @param duration the duration to check. + * @return a valid duration. + */ + private static long checkDuration(final long duration) { + return duration > 0L ? duration : 15L * 60L * 1000L; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/logging/LogManagerProperties.java b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/LogManagerProperties.java new file mode 100644 index 000000000..25b2072f5 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/LogManagerProperties.java @@ -0,0 +1,1114 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2009-2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009-2017 Jason Mehrens. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package com.sun.mail.util.logging; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.*; +import java.util.logging.*; +import java.util.logging.Formatter; + +/** + * An adapter class to allow the Mail API to access the LogManager properties. + * The LogManager properties are treated as the root of all properties. First, + * the parent properties are searched. If no value is found, then, the + * LogManager is searched with prefix value. If not found, then, just the key + * itself is searched in the LogManager. If a value is found in the LogManager + * it is then copied to this properties object with no key prefix. If no value + * is found in the LogManager or the parent properties, then this properties + * object is searched only by passing the key value. + * + *

    + * This class also emulates the LogManager functions for creating new objects + * from string class names. This is to support initial setup of objects such as + * log filters, formatters, error managers, etc. + * + *

    + * This class should never be exposed outside of this package. Keep this class + * package private (default access). + * + * @author Jason Mehrens + * @since JavaMail 1.4.3 + */ +final class LogManagerProperties extends Properties { + + /** + * Generated serial id. + */ + private static final long serialVersionUID = -2239983349056806252L; + /** + * Holds the method used to get the LogRecord instant if running on JDK 9 or + * later. + */ + private static final Method LR_GET_INSTANT; + + /** + * Holds the method used to get the default time zone if running on JDK 9 or + * later. + */ + private static final Method ZI_SYSTEM_DEFAULT; + + /** + * Holds the method used to convert and instant to a zoned date time if + * running on JDK 9 later. + */ + private static final Method ZDT_OF_INSTANT; + + static { + Method lrgi = null; + Method zisd = null; + Method zdtoi = null; + try { + lrgi = LogRecord.class.getMethod("getInstant"); + assert Comparable.class + .isAssignableFrom(lrgi.getReturnType()) : lrgi; + zisd = findClass("java.time.ZoneId") + .getMethod("systemDefault"); + if (!Modifier.isStatic(zisd.getModifiers())) { + throw new NoSuchMethodException(zisd.toString()); + } + + zdtoi = findClass("java.time.ZonedDateTime") + .getMethod("ofInstant", findClass("java.time.Instant"), + findClass("java.time.ZoneId")); + if (!Modifier.isStatic(zdtoi.getModifiers())) { + throw new NoSuchMethodException(zdtoi.toString()); + } + + if (!Comparable.class.isAssignableFrom(zdtoi.getReturnType())) { + throw new NoSuchMethodException(zdtoi.toString()); + } + } catch (final RuntimeException ignore) { + } catch (final Exception ignore) { //No need for specific catch. + } catch (final LinkageError ignore) { + } finally { + if (lrgi == null || zisd == null || zdtoi == null) { + lrgi = null; //If any are null then clear all. + zisd = null; + zdtoi = null; + } + } + + LR_GET_INSTANT = lrgi; + ZI_SYSTEM_DEFAULT = zisd; + ZDT_OF_INSTANT = zdtoi; + } + /** + * Caches the read only reflection class names string array. Declared + * volatile for safe publishing only. The VO_VOLATILE_REFERENCE_TO_ARRAY + * warning is a false positive. + */ + @SuppressWarnings("VolatileArrayField") + private static volatile String[] REFLECT_NAMES; + /** + * Caches the LogManager or Properties so we only read the configuration + * once. + */ + private static final Object LOG_MANAGER = loadLogManager(); + + /** + * Get the LogManager or loads a Properties object to use as the LogManager. + * + * @return the LogManager or a loaded Properties object. + * @since JavaMail 1.5.3 + */ + private static Object loadLogManager() { + Object m; + try { + m = LogManager.getLogManager(); + } catch (final LinkageError restricted) { + m = readConfiguration(); + } catch (final RuntimeException unexpected) { + m = readConfiguration(); + } + return m; + } + + /** + * Create a properties object from the default logging configuration file. + * Since the LogManager is not available in restricted environments, only + * the default configuration is applicable. + * + * @return a properties object loaded with the default configuration. + * @since JavaMail 1.5.3 + */ + private static Properties readConfiguration() { + /** + * Load the properties file so the default settings are available when + * user code creates a logging object. The class loader for the + * restricted LogManager can't access these classes to attach them to a + * logger or handler on startup. Creating logging objects at this point + * is both useless and risky. + */ + final Properties props = new Properties(); + try { + String n = System.getProperty("java.util.logging.config.file"); + if (n != null) { + final File f = new File(n).getCanonicalFile(); + final InputStream in = new FileInputStream(f); + try { + props.load(in); + } finally { + in.close(); + } + } + } catch (final RuntimeException permissionsOrMalformed) { + } catch (final Exception ioe) { + } catch (final LinkageError unexpected) { + } + return props; + } + + /** + * Gets LogManger property for the running JVM. If the LogManager doesn't + * exist then the default LogManger properties are used. + * + * @param name the property name. + * @return the LogManager. + * @throws NullPointerException if the given name is null. + * @since JavaMail 1.5.3 + */ + static String fromLogManager(final String name) { + if (name == null) { + throw new NullPointerException(); + } + + final Object m = LOG_MANAGER; + try { + if (m instanceof Properties) { + return ((Properties) m).getProperty(name); + } + } catch (final RuntimeException unexpected) { + } + + if (m != null) { + try { + if (m instanceof LogManager) { + return ((LogManager) m).getProperty(name); + } + } catch (final LinkageError restricted) { + } catch (final RuntimeException unexpected) { + } + } + return null; + } + + /** + * Check that the current context is trusted to modify the logging + * configuration. This requires LoggingPermission("control"). + * @throws SecurityException if a security manager exists and the caller + * does not have {@code LoggingPermission("control")}. + * @since JavaMail 1.5.3 + */ + static void checkLogManagerAccess() { + boolean checked = false; + final Object m = LOG_MANAGER; + if (m != null) { + try { + if (m instanceof LogManager) { + checked = true; + ((LogManager) m).checkAccess(); + } + } catch (final SecurityException notAllowed) { + if (checked) { + throw notAllowed; + } + } catch (final LinkageError restricted) { + } catch (final RuntimeException unexpected) { + } + } + + if (!checked) { + checkLoggingAccess(); + } + } + + /** + * Check that the current context is trusted to modify the logging + * configuration when the LogManager is not present. This requires + * LoggingPermission("control"). + * @throws SecurityException if a security manager exists and the caller + * does not have {@code LoggingPermission("control")}. + * @since JavaMail 1.5.3 + */ + private static void checkLoggingAccess() { + /** + * Some environments selectively enforce logging permissions by allowing + * access to loggers but not allowing access to handlers. This is an + * indirect way of checking for LoggingPermission when the LogManager is + * not present. The root logger will lazy create handlers so the global + * logger is used instead as it is a known named logger with well + * defined behavior. If the global logger is a subclass then fallback to + * using the SecurityManager. + */ + boolean checked = false; + final Logger global = Logger.getLogger("global"); + try { + if (Logger.class == global.getClass()) { + global.removeHandler((Handler) null); + checked = true; + } + } catch (final NullPointerException unexpected) { + } + + if (!checked) { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new LoggingPermission("control", null)); + } + } + } + + /** + * Determines if access to the {@code java.util.logging.LogManager} class is + * restricted by the class loader. + * + * @return true if a LogManager is present. + * @since JavaMail 1.5.3 + */ + static boolean hasLogManager() { + final Object m = LOG_MANAGER; + return m != null && !(m instanceof Properties); + } + + /** + * Gets the ZonedDateTime from the given log record. + * + * @param record used to generate the zoned date time. + * @return null if LogRecord doesn't support nanoseconds otherwise a new + * zoned date time is returned. + * @throws NullPointerException if record is null. + * @since JavaMail 1.5.6 + */ + @SuppressWarnings("UseSpecificCatch") + static Comparable getZonedDateTime(LogRecord record) { + if (record == null) { + throw new NullPointerException(); + } + final Method m = ZDT_OF_INSTANT; + if (m != null) { + try { + return (Comparable) m.invoke((Object) null, + LR_GET_INSTANT.invoke(record), + ZI_SYSTEM_DEFAULT.invoke((Object) null)); + } catch (final RuntimeException ignore) { + assert LR_GET_INSTANT != null + && ZI_SYSTEM_DEFAULT != null : ignore; + } catch (final InvocationTargetException ite) { + final Throwable cause = ite.getCause(); + if (cause instanceof Error) { + throw (Error) cause; + } else if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } else { //Should never happen. + throw new UndeclaredThrowableException(ite); + } + } catch (final Exception ignore) { + } + } + return null; + } + + /** + * Gets the local host name from the given service. + * + * @param s the service to examine. + * @return the local host name or null. + * @throws IllegalAccessException if the method is inaccessible. + * @throws InvocationTargetException if the method throws an exception. + * @throws LinkageError if the linkage fails. + * @throws NullPointerException if the given service is null. + * @throws ExceptionInInitializerError if the static initializer fails. + * @throws Exception if there is a problem. + * @throws NoSuchMethodException if the given service does not have a method + * to get the local host name as a string. + * @throws SecurityException if unable to inspect properties of object. + * @since JavaMail 1.5.3 + */ + static String getLocalHost(final Object s) throws Exception { + try { + final Method m = s.getClass().getMethod("getLocalHost"); + if (!Modifier.isStatic(m.getModifiers()) + && m.getReturnType() == String.class) { + return (String) m.invoke(s); + } else { + throw new NoSuchMethodException(m.toString()); + } + } catch (final ExceptionInInitializerError EIIE) { + throw wrapOrThrow(EIIE); + } catch (final InvocationTargetException ite) { + throw paramOrError(ite); + } + } + + /** + * Used to parse an ISO-8601 duration format of {@code PnDTnHnMn.nS}. + * + * @param value an ISO-8601 duration character sequence. + * @return the number of milliseconds parsed from the duration. + * @throws ClassNotFoundException if the java.time classes are not present. + * @throws IllegalAccessException if the method is inaccessible. + * @throws InvocationTargetException if the method throws an exception. + * @throws LinkageError if the linkage fails. + * @throws NullPointerException if the given duration is null. + * @throws ExceptionInInitializerError if the static initializer fails. + * @throws Exception if there is a problem. + * @throws NoSuchMethodException if the correct time methods are missing. + * @throws SecurityException if reflective access to the java.time classes + * are not allowed. + * @since JavaMail 1.5.5 + */ + static long parseDurationToMillis(final CharSequence value) throws Exception { + try { + final Class k = findClass("java.time.Duration"); + final Method parse = k.getMethod("parse", CharSequence.class); + if (!k.isAssignableFrom(parse.getReturnType()) + || !Modifier.isStatic(parse.getModifiers())) { + throw new NoSuchMethodException(parse.toString()); + } + + final Method toMillis = k.getMethod("toMillis"); + if (!Long.TYPE.isAssignableFrom(toMillis.getReturnType()) + || Modifier.isStatic(toMillis.getModifiers())) { + throw new NoSuchMethodException(toMillis.toString()); + } + return (Long) toMillis.invoke(parse.invoke(null, value)); + } catch (final ExceptionInInitializerError EIIE) { + throw wrapOrThrow(EIIE); + } catch (final InvocationTargetException ite) { + throw paramOrError(ite); + } + } + + /** + * Converts a locale to a language tag. + * + * @param locale the locale to convert. + * @return the language tag. + * @throws NullPointerException if the given locale is null. + * @since JavaMail 1.4.5 + */ + static String toLanguageTag(final Locale locale) { + final String l = locale.getLanguage(); + final String c = locale.getCountry(); + final String v = locale.getVariant(); + final char[] b = new char[l.length() + c.length() + v.length() + 2]; + int count = l.length(); + l.getChars(0, count, b, 0); + if (c.length() != 0 || (l.length() != 0 && v.length() != 0)) { + b[count] = '-'; + ++count; //be nice to the client compiler. + c.getChars(0, c.length(), b, count); + count += c.length(); + } + + if (v.length() != 0 && (l.length() != 0 || c.length() != 0)) { + b[count] = '-'; + ++count; //be nice to the client compiler. + v.getChars(0, v.length(), b, count); + count += v.length(); + } + return String.valueOf(b, 0, count); + } + + /** + * Creates a new filter from the given class name. + * + * @param name the fully qualified class name. + * @return a new filter. + * @throws ClassCastException if class name does not match the type. + * @throws ClassNotFoundException if the class name was not found. + * @throws IllegalAccessException if the constructor is inaccessible. + * @throws InstantiationException if the given class name is abstract. + * @throws InvocationTargetException if the constructor throws an exception. + * @throws LinkageError if the linkage fails. + * @throws ExceptionInInitializerError if the static initializer fails. + * @throws Exception to match the error method of the ErrorManager. + * @throws NoSuchMethodException if the class name does not have a no + * argument constructor. + * @since JavaMail 1.4.5 + */ + static Filter newFilter(String name) throws Exception { + return newObjectFrom(name, Filter.class); + } + + /** + * Creates a new formatter from the given class name. + * + * @param name the fully qualified class name. + * @return a new formatter. + * @throws ClassCastException if class name does not match the type. + * @throws ClassNotFoundException if the class name was not found. + * @throws IllegalAccessException if the constructor is inaccessible. + * @throws InstantiationException if the given class name is abstract. + * @throws InvocationTargetException if the constructor throws an exception. + * @throws LinkageError if the linkage fails. + * @throws ExceptionInInitializerError if the static initializer fails. + * @throws Exception to match the error method of the ErrorManager. + * @throws NoSuchMethodException if the class name does not have a no + * argument constructor. + * @since JavaMail 1.4.5 + */ + static Formatter newFormatter(String name) throws Exception { + return newObjectFrom(name, Formatter.class); + } + + /** + * Creates a new log record comparator from the given class name. + * + * @param name the fully qualified class name. + * @return a new comparator. + * @throws ClassCastException if class name does not match the type. + * @throws ClassNotFoundException if the class name was not found. + * @throws IllegalAccessException if the constructor is inaccessible. + * @throws InstantiationException if the given class name is abstract. + * @throws InvocationTargetException if the constructor throws an exception. + * @throws LinkageError if the linkage fails. + * @throws ExceptionInInitializerError if the static initializer fails. + * @throws Exception to match the error method of the ErrorManager. + * @throws NoSuchMethodException if the class name does not have a no + * argument constructor. + * @since JavaMail 1.4.5 + * @see java.util.logging.LogRecord + */ + @SuppressWarnings("unchecked") + static Comparator newComparator(String name) throws Exception { + return newObjectFrom(name, Comparator.class); + } + + /** + * Returns a comparator that imposes the reverse ordering of the specified + * {@link Comparator}. If the given comparator declares a public + * reverseOrder method that method is called first and the return value is + * used. If that method is not declared or the caller does not have access + * then a comparator wrapping the given comparator is returned. + * + * @param the element type to be compared + * @param c a comparator whose ordering is to be reversed by the returned + * comparator + * @return A comparator that imposes the reverse ordering of the specified + * comparator. + * @throws NullPointerException if the given comparator is null. + * @since JavaMail 1.5.0 + */ + @SuppressWarnings({"unchecked", "ThrowableResultIgnored"}) + static Comparator reverseOrder(final Comparator c) { + if (c == null) { + throw new NullPointerException(); + } + + Comparator reverse = null; + //Comparator in Java 1.8 has 'reversed' as a default method. + //This code calls that method first to allow custom + //code to define what reverse order means. + try { + //assert Modifier.isPublic(c.getClass().getModifiers()) : + // Modifier.toString(c.getClass().getModifiers()); + final Method m = c.getClass().getMethod("reversed"); + if (!Modifier.isStatic(m.getModifiers()) + && Comparator.class.isAssignableFrom(m.getReturnType())) { + try { + reverse = (Comparator) m.invoke(c); + } catch (final ExceptionInInitializerError eiie) { + throw wrapOrThrow(eiie); + } + } + } catch (final NoSuchMethodException ignore) { + } catch (final IllegalAccessException ignore) { + } catch (final RuntimeException ignore) { + } catch (final InvocationTargetException ite) { + paramOrError(ite); //Ignore invocation bugs (returned values). + } + + if (reverse == null) { + reverse = Collections.reverseOrder(c); + } + return reverse; + } + + /** + * Creates a new error manager from the given class name. + * + * @param name the fully qualified class name. + * @return a new error manager. + * @throws ClassCastException if class name does not match the type. + * @throws ClassNotFoundException if the class name was not found. + * @throws IllegalAccessException if the constructor is inaccessible. + * @throws InstantiationException if the given class name is abstract. + * @throws InvocationTargetException if the constructor throws an exception. + * @throws LinkageError if the linkage fails. + * @throws ExceptionInInitializerError if the static initializer fails. + * @throws Exception to match the error method of the ErrorManager. + * @throws NoSuchMethodException if the class name does not have a no + * argument constructor. + * @since JavaMail 1.4.5 + */ + static ErrorManager newErrorManager(String name) throws Exception { + return newObjectFrom(name, ErrorManager.class); + } + + /** + * Determines if the given class name identifies a utility class. + * + * @param name the fully qualified class name. + * @return true if the given class name + * @throws ClassNotFoundException if the class name was not found. + * @throws IllegalAccessException if the constructor is inaccessible. + * @throws LinkageError if the linkage fails. + * @throws ExceptionInInitializerError if the static initializer fails. + * @throws Exception to match the error method of the ErrorManager. + * @throws SecurityException if unable to inspect properties of class. + * @since JavaMail 1.5.2 + */ + static boolean isStaticUtilityClass(String name) throws Exception { + final Class c = findClass(name); + final Class obj = Object.class; + Method[] methods; + boolean util; + if (c != obj && (methods = c.getMethods()).length != 0) { + util = true; + for (Method m : methods) { + if (m.getDeclaringClass() != obj + && !Modifier.isStatic(m.getModifiers())) { + util = false; + break; + } + } + } else { + util = false; + } + return util; + } + + /** + * Determines if the given class name is a reflection class name responsible + * for invoking methods and or constructors. + * + * @param name the fully qualified class name. + * @return true if the given class name + * @throws ClassNotFoundException if the class name was not found. + * @throws IllegalAccessException if the constructor is inaccessible. + * @throws LinkageError if the linkage fails. + * @throws ExceptionInInitializerError if the static initializer fails. + * @throws Exception to match the error method of the ErrorManager. + * @throws SecurityException if unable to inspect properties of class. + * @since JavaMail 1.5.2 + */ + static boolean isReflectionClass(String name) throws Exception { + String[] names = REFLECT_NAMES; + if (names == null) { //Benign data race. + REFLECT_NAMES = names = reflectionClassNames(); + } + + for (String rf : names) { //The set of names is small. + if (name.equals(rf)) { + return true; + } + } + + findClass(name); //Fail late instead of normal return. + return false; + } + + /** + * Determines all of the reflection class names used to invoke methods. + * + * This method performs indirect and direct calls on a throwable to capture + * the standard class names and the implementation class names. + * + * @return a string array containing the fully qualified class names. + * @throws Exception if there is a problem. + */ + private static String[] reflectionClassNames() throws Exception { + final Class thisClass = LogManagerProperties.class; + assert Modifier.isFinal(thisClass.getModifiers()) : thisClass; + try { + final HashSet traces = new HashSet<>(); + Throwable t = Throwable.class.getConstructor().newInstance(); + for (StackTraceElement ste : t.getStackTrace()) { + if (!thisClass.getName().equals(ste.getClassName())) { + traces.add(ste.getClassName()); + } else { + break; + } + } + + Throwable.class.getMethod("fillInStackTrace").invoke(t); + for (StackTraceElement ste : t.getStackTrace()) { + if (!thisClass.getName().equals(ste.getClassName())) { + traces.add(ste.getClassName()); + } else { + break; + } + } + return traces.toArray(new String[traces.size()]); + } catch (final InvocationTargetException ITE) { + throw paramOrError(ITE); + } + } + + /** + * Creates a new object from the given class name. + * + * @param The generic class type. + * @param name the fully qualified class name. + * @param type the assignable type for the given name. + * @return a new object assignable to the given type. + * @throws ClassCastException if class name does not match the type. + * @throws ClassNotFoundException if the class name was not found. + * @throws IllegalAccessException if the constructor is inaccessible. + * @throws InstantiationException if the given class name is abstract. + * @throws InvocationTargetException if the constructor throws an exception. + * @throws LinkageError if the linkage fails. + * @throws ExceptionInInitializerError if the static initializer fails. + * @throws Exception to match the error method of the ErrorManager. + * @throws NoSuchMethodException if the class name does not have a no + * argument constructor. + * @since JavaMail 1.4.5 + */ + static T newObjectFrom(String name, Class type) throws Exception { + try { + final Class clazz = LogManagerProperties.findClass(name); + //This check avoids additional side effects when the name parameter + //is a literal name and not a class name. + if (type.isAssignableFrom(clazz)) { + try { + return type.cast(clazz.getConstructor().newInstance()); + } catch (final InvocationTargetException ITE) { + throw paramOrError(ITE); + } + } else { + throw new ClassCastException(clazz.getName() + + " cannot be cast to " + type.getName()); + } + } catch (final NoClassDefFoundError NCDFE) { + //No class def found can occur on filesystems that are + //case insensitive (BUG ID 6196068). In some cases, we allow class + //names or literal names, this code guards against the case where a + //literal name happens to match a class name in a different case. + //This is also a nice way to adapt this error for the error manager. + throw new ClassNotFoundException(NCDFE.toString(), NCDFE); + } catch (final ExceptionInInitializerError EIIE) { + throw wrapOrThrow(EIIE); + } + } + + /** + * Returns the given exception or throws the escaping cause. + * + * @param ite any invocation target. + * @return the exception. + * @throws VirtualMachineError if present as cause. + * @throws ThreadDeath if present as cause. + * @since JavaMail 1.4.5 + */ + private static Exception paramOrError(InvocationTargetException ite) { + final Throwable cause = ite.getCause(); + if (cause != null) { + //Bitwise inclusive OR produces tighter bytecode for instanceof + //and matches with multicatch syntax. + if (cause instanceof VirtualMachineError + | cause instanceof ThreadDeath) { + throw (Error) cause; + } + } + return ite; + } + + /** + * Throws the given error if the cause is an error otherwise the given error + * is wrapped. + * + * @param eiie the error. + * @return an InvocationTargetException. + * @since JavaMail 1.5.0 + */ + private static InvocationTargetException wrapOrThrow( + ExceptionInInitializerError eiie) { + //This linkage error will escape the constructor new instance call. + //If the cause is an error, rethrow to skip any error manager. + if (eiie.getCause() instanceof Error) { + throw eiie; + } else { + //Considered a bug in the code, wrap the error so it can be + //reported to the error manager. + return new InvocationTargetException(eiie); + } + } + + /** + * This code is modified from the LogManager, which explictly states + * searching the system class loader first, then the context class loader. + * There is resistance (compatibility) to change this behavior to simply + * searching the context class loader. + * + * @param name full class name + * @return the class. + * @throws LinkageError if the linkage fails. + * @throws ClassNotFoundException if the class name was not found. + * @throws ExceptionInInitializerError if static initializer fails. + */ + private static Class findClass(String name) throws ClassNotFoundException { + ClassLoader[] loaders = getClassLoaders(); + assert loaders.length == 2 : loaders.length; + Class clazz; + if (loaders[0] != null) { + try { + clazz = Class.forName(name, false, loaders[0]); + } catch (ClassNotFoundException tryContext) { + clazz = tryLoad(name, loaders[1]); + } + } else { + clazz = tryLoad(name, loaders[1]); + } + return clazz; + } + + /** + * Loads a class using the given loader or the class loader of this class. + * + * @param name the class name. + * @param l any class loader or null. + * @return the raw class. + * @throws ClassNotFoundException if not found. + */ + private static Class tryLoad(String name, ClassLoader l) throws ClassNotFoundException { + if (l != null) { + return Class.forName(name, false, l); + } else { + return Class.forName(name); + } + } + + /** + * Gets the class loaders using elevated privileges. + * + * @return any array of class loaders. Indexes may be null. + */ + private static ClassLoader[] getClassLoaders() { + return AccessController.doPrivileged(new PrivilegedAction() { + + @SuppressWarnings("override") //JDK-6954234 + public ClassLoader[] run() { + final ClassLoader[] loaders = new ClassLoader[2]; + try { + loaders[0] = ClassLoader.getSystemClassLoader(); + } catch (SecurityException ignore) { + loaders[0] = null; + } + + try { + loaders[1] = Thread.currentThread().getContextClassLoader(); + } catch (SecurityException ignore) { + loaders[1] = null; + } + return loaders; + } + }); + } + /** + * The namespace prefix to search LogManager and defaults. + */ + private final String prefix; + + /** + * Creates a log manager properties object. + * + * @param parent the parent properties. + * @param prefix the namespace prefix. + * @throws NullPointerException if prefix or parent is + * null. + */ + LogManagerProperties(final Properties parent, final String prefix) { + super(parent); + if (parent == null || prefix == null) { + throw new NullPointerException(); + } + this.prefix = prefix; + } + + /** + * Returns a properties object that contains a snapshot of the current + * state. This method violates the clone contract so that no instances of + * LogManagerProperties is exported for public use. + * + * @return the snapshot. + * @since JavaMail 1.4.4 + */ + @Override + @SuppressWarnings("CloneDoesntCallSuperClone") + public synchronized Object clone() { + return exportCopy(defaults); + } + + /** + * Searches defaults, then searches the log manager if available or the + * system properties by the prefix property, and then by the key itself. + * + * @param key a non null key. + * @return the value for that key. + */ + @Override + public synchronized String getProperty(final String key) { + String value = defaults.getProperty(key); + if (value == null) { + if (key.length() > 0) { + value = fromLogManager(prefix + '.' + key); + } + + if (value == null) { + value = fromLogManager(key); + } + + /** + * Copy the log manager properties as we read them. If a value is no + * longer present in the LogManager read it from here. The reason + * this works is because LogManager.reset() closes all attached + * handlers therefore, stale values only exist in closed handlers. + */ + if (value != null) { + super.put(key, value); + } else { + Object v = super.get(key); //defaults are not used. + value = v instanceof String ? (String) v : null; + } + } + return value; + } + + /** + * Calls getProperty directly. If getProperty returns null the default value + * is returned. + * + * @param key a key to search for. + * @param def the default value to use if not found. + * @return the value for the key. + * @since JavaMail 1.4.4 + */ + @Override + public String getProperty(final String key, final String def) { + final String value = this.getProperty(key); + return value == null ? def : value; + } + + /** + * Required to work with PropUtil. Calls getProperty directly if the given + * key is a string. Otherwise, performs a get operation on the defaults + * followed by the normal hash table get. + * + * @param key any key. + * @return the value for the key or null. + * @since JavaMail 1.4.5 + */ + @Override + public synchronized Object get(final Object key) { + Object value; + if (key instanceof String) { + value = getProperty((String) key); + } else { + value = null; + } + + //Search for non-string value. + if (value == null) { + value = defaults.get(key); + if (value == null && !defaults.containsKey(key)) { + value = super.get(key); + } + } + return value; + } + + /** + * Required to work with PropUtil. An updated copy of the key is fetched + * from the log manager if the key doesn't exist in this properties. + * + * @param key any key. + * @return the value for the key or the default value for the key. + * @since JavaMail 1.4.5 + */ + @Override + public synchronized Object put(final Object key, final Object value) { + if (key instanceof String && value instanceof String) { + final Object def = preWrite(key); + final Object man = super.put(key, value); + return man == null ? def : man; + } else { + return super.put(key, value); + } + } + + /** + * Calls the put method directly. + * + * @param key any key. + * @return the value for the key or the default value for the key. + * @since JavaMail 1.4.5 + */ + @Override + public Object setProperty(String key, String value) { + return this.put(key, value); + } + + /** + * Required to work with PropUtil. An updated copy of the key is fetched + * from the log manager prior to returning. + * + * @param key any key. + * @return the value for the key or null. + * @since JavaMail 1.4.5 + */ + @Override + public synchronized boolean containsKey(final Object key) { + boolean found = key instanceof String + && getProperty((String) key) != null; + if (!found) { + found = defaults.containsKey(key) || super.containsKey(key); + } + return found; + } + + /** + * Required to work with PropUtil. An updated copy of the key is fetched + * from the log manager if the key doesn't exist in this properties. + * + * @param key any key. + * @return the value for the key or the default value for the key. + * @since JavaMail 1.4.5 + */ + @Override + public synchronized Object remove(final Object key) { + final Object def = preWrite(key); + final Object man = super.remove(key); + return man == null ? def : man; + } + + /** + * It is assumed that this method will never be called. No way to get the + * property names from LogManager. + * + * @return the property names + */ + @Override + public Enumeration propertyNames() { + assert false; + return super.propertyNames(); + } + + /** + * It is assumed that this method will never be called. The prefix value is + * not used for the equals method. + * + * @param o any object or null. + * @return true if equal, otherwise false. + */ + @Override + public boolean equals(final Object o) { + if (o == null) { + return false; + } + if (o == this) { + return true; + } + if (o instanceof Properties == false) { + return false; + } + assert false : prefix; + return super.equals(o); + } + + /** + * It is assumed that this method will never be called. See equals. + * + * @return the hash code. + */ + @Override + public int hashCode() { + assert false : prefix.hashCode(); + return super.hashCode(); + } + + /** + * Called before a write operation of a key. Caches a key read from the log + * manager in this properties object. The key is only cached if it is an + * instance of a String and this properties doesn't contain a copy of the + * key. + * + * @param key the key to search. + * @return the default value for the key. + */ + private Object preWrite(final Object key) { + assert Thread.holdsLock(this); + return get(key); + } + + /** + * Creates a public snapshot of this properties object using the given + * parent properties. + * + * @param parent the defaults to use with the snapshot. + * @return the safe snapshot. + */ + private Properties exportCopy(final Properties parent) { + Thread.holdsLock(this); + final Properties child = new Properties(parent); + child.putAll(this); + return child; + } + + /** + * It is assumed that this method will never be called. We return a safe + * copy for export to avoid locking this properties object or the defaults + * during write. + * + * @return the parent properties. + * @throws ObjectStreamException if there is a problem. + */ + private synchronized Object writeReplace() throws ObjectStreamException { + assert false; + return exportCopy((Properties) defaults.clone()); + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/logging/MailHandler.java b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/MailHandler.java new file mode 100644 index 000000000..e6879ba66 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/MailHandler.java @@ -0,0 +1,4420 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2009-2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009-2017 Jason Mehrens. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package com.sun.mail.util.logging; + +import static com.sun.mail.util.logging.LogManagerProperties.fromLogManager; +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.net.InetAddress; +import java.net.URLConnection; +import java.net.UnknownHostException; +import java.nio.charset.Charset; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.*; +import java.util.logging.*; +import java.util.logging.Formatter; +import javax.activation.*; +import javax.mail.*; +import javax.mail.internet.*; +import javax.mail.util.ByteArrayDataSource; + +/** + * Handler that formats log records as an email message. + * + *

    + * This Handler will store a fixed number of log records used to + * generate a single email message. When the internal buffer reaches capacity, + * all log records are formatted and placed in an email which is sent to an + * email server. The code to manually setup this handler can be as simple as + * the following: + * + *

    + *      Properties props = new Properties();
    + *      props.put("mail.smtp.host", "my-mail-server");
    + *      props.put("mail.to", "me@example.com");
    + *      props.put("verify", "local");
    + *      MailHandler h = new MailHandler(props);
    + *      h.setLevel(Level.WARNING);
    + * 
    + * + *

    + * Configuration: + * The LogManager should define at least one or more recipient addresses and a + * mail host for outgoing email. The code to setup this handler via the + * logging properties can be as simple as the following: + * + *

    + *      #Default MailHandler settings.
    + *      com.sun.mail.util.logging.MailHandler.mail.smtp.host = my-mail-server
    + *      com.sun.mail.util.logging.MailHandler.mail.to = me@example.com
    + *      com.sun.mail.util.logging.MailHandler.level = WARNING
    + *      com.sun.mail.util.logging.MailHandler.verify = local
    + * 
    + * + * For a custom handler, e.g. com.foo.MyHandler, the properties would + * be: + * + *
    + *      #Subclass com.foo.MyHandler settings.
    + *      com.foo.MyHandler.mail.smtp.host = my-mail-server
    + *      com.foo.MyHandler.mail.to = me@example.com
    + *      com.foo.MyHandler.level = WARNING
    + *      com.foo.MyHandler.verify = local
    + * 
    + * + * All mail properties documented in the Java Mail API cascade to the + * LogManager by prefixing a key using the fully qualified class name of this + * MailHandler or the fully qualified derived class name dot mail + * property. If the prefixed property is not found, then the mail property + * itself is searched in the LogManager. By default each MailHandler is + * initialized using the following LogManager configuration properties where + * <handler-name> refers to the fully qualified class name of the + * handler. If properties are not defined, or contain invalid values, then the + * specified default values are used. + * + *
      + *
    • <handler-name>.attachment.filters a comma + * separated list of Filter class names used to create each attachment. + * The literal null is reserved for attachments that do not require + * filtering. (defaults to the + * {@linkplain java.util.logging.Handler#getFilter() body} filter) + * + *
    • <handler-name>.attachment.formatters a comma + * separated list of Formatter class names used to create each + * attachment. (default is no attachments) + * + *
    • <handler-name>.attachment.names a comma separated + * list of names or Formatter class names of each attachment. All + * control characters are removed from the attachment names. + * (default is {@linkplain java.util.logging.Formatter#toString() toString} + * of the attachment formatter) + * + *
    • <handler-name>.authenticator name of an + * {@linkplain javax.mail.Authenticator} class used to provide login credentials + * to the email server or string literal that is the password used with the + * {@linkplain Authenticator#getDefaultUserName() default} user name. + * (default is null) + * + *
    • <handler-name>.capacity the max number of + * LogRecord objects include in each email message. + * (defaults to 1000) + * + *
    • <handler-name>.comparator name of a + * {@linkplain java.util.Comparator} class used to sort the published + * LogRecord objects prior to all formatting. + * (defaults to null meaning records are unsorted). + * + *
    • <handler-name>.comparator.reverse a boolean + * true to reverse the order of the specified comparator or + * false to retain the original order. (defaults to false) + * + *
    • <handler-name>.encoding the name of the Java + * {@linkplain java.nio.charset.Charset#name() character set} to use for the + * email message. (defaults to null, the + * {@linkplain javax.mail.internet.MimeUtility#getDefaultJavaCharset() default} + * platform encoding). + * + *
    • <handler-name>.errorManager name of an + * ErrorManager class used to handle any configuration or mail + * transport problems. (defaults to java.util.logging.ErrorManager) + * + *
    • <handler-name>.filter name of a Filter + * class used for the body of the message. (defaults to null, + * allow all records) + * + *
    • <handler-name>.formatter name of a + * Formatter class used to format the body of this message. + * (defaults to java.util.logging.SimpleFormatter) + * + *
    • <handler-name>.level specifies the default level + * for this Handler (defaults to Level.WARNING). + * + *
    • <handler-name>.mail.bcc a comma separated list of + * addresses which will be blind carbon copied. Typically, this is set to the + * recipients that may need to be privately notified of a log message or + * notified that a log message was sent to a third party such as a support team. + * The empty string can be used to specify no blind carbon copied address. + * (defaults to null, none) + * + *
    • <handler-name>.mail.cc a comma separated list of + * addresses which will be carbon copied. Typically, this is set to the + * recipients that may need to be notified of a log message but, are not + * required to provide direct support. The empty string can be used to specify + * no carbon copied address. (defaults to null, none) + * + *
    • <handler-name>.mail.from a comma separated list of + * addresses which will be from addresses. Typically, this is set to the email + * address identifying the user running the application. The empty string can + * be used to override the default behavior and specify no from address. + * (defaults to the {@linkplain javax.mail.Message#setFrom() local address}) + * + *
    • <handler-name>.mail.host the host name or IP + * address of the email server. (defaults to null, use + * {@linkplain Transport#protocolConnect default} + * Java Mail behavior) + * + *
    • <handler-name>.mail.reply.to a comma separated + * list of addresses which will be reply-to addresses. Typically, this is set + * to the recipients that provide support for the application itself. The empty + * string can be used to specify no reply-to address. + * (defaults to null, none) + * + *
    • <handler-name>.mail.to a comma separated list of + * addresses which will be send-to addresses. Typically, this is set to the + * recipients that provide support for the application, system, and/or + * supporting infrastructure. The empty string can be used to specify no + * send-to address which overrides the default behavior. (defaults to + * {@linkplain javax.mail.internet.InternetAddress#getLocalAddress + * local address}.) + * + *
    • <handler-name>.mail.sender a single address + * identifying sender of the email; never equal to the from address. Typically, + * this is set to the email address identifying the application itself. The + * empty string can be used to specify no sender address. + * (defaults to null, none) + * + *
    • <handler-name>.subject the name of a + * Formatter class or string literal used to create the subject line. + * The empty string can be used to specify no subject. All control characters + * are removed from the subject line. (defaults to {@linkplain + * com.sun.mail.util.logging.CollectorFormatter CollectorFormatter}.) + * + *
    • <handler-name>.pushFilter the name of a + * Filter class used to trigger an early push. + * (defaults to null, no early push) + * + *
    • <handler-name>.pushLevel the level which will + * trigger an early push. (defaults to Level.OFF, only push when full) + * + *
    • <handler-name>.verify used to + * verify the Handler configuration prior to a push. + *
        + *
      • If the value is not set, equal to an empty string, or equal to the + * literal null then no settings are verified prior to a push. + *
      • If set to a value of limited then the Handler will + * verify minimal local machine settings. + *
      • If set to a value of local the Handler will verify + * all of settings of the local machine. + *
      • If set to a value of resolve, the Handler will + * verify all local settings and try to resolve the remote host name with + * the domain name server. + *
      • If set to a value of login, the Handler will + * verify all local settings and try to establish a connection with + * the email server. + *
      • If set to a value of remote, the Handler will + * verify all local settings, try to establish a connection with the + * email server, and try to verify the envelope of the email message. + *
      + * If this Handler is only implicitly closed by the + * LogManager, then verification should be turned on. + * (defaults to null, no verify). + *
    + * + *

    + * Normalization: + * The error manager, filters, and formatters when loaded from the LogManager + * are converted into canonical form inside the MailHandler. The pool of + * interned values is limited to each MailHandler object such that no two + * MailHandler objects created by the LogManager will be created sharing + * identical error managers, filters, or formatters. If a filter or formatter + * should not be interned then it is recommended to retain the identity + * equals and identity hashCode methods as the implementation. For a filter or + * formatter to be interned the class must implement the + * {@linkplain java.lang.Object#equals(java.lang.Object) equals} + * and {@linkplain java.lang.Object#hashCode() hashCode} methods. + * The recommended code to use for stateless filters and formatters is: + *

    + * public boolean equals(Object obj) {
    + *     return obj == null ? false : obj.getClass() == getClass();
    + * }
    + *
    + * public int hashCode() {
    + *     return 31 * getClass().hashCode();
    + * }
    + * 
    + * + *

    + * Sorting: + * All LogRecord objects are ordered prior to formatting if this + * Handler has a non null comparator. Developers might be interested + * in sorting the formatted email by thread id, time, and sequence properties + * of a LogRecord. Where as system administrators might be interested + * in sorting the formatted email by thrown, level, time, and sequence + * properties of a LogRecord. If comparator for this handler is + * null then the order is unspecified. + * + *

    + * Formatting: + * The main message body is formatted using the Formatter returned by + * getFormatter(). Only records that pass the filter returned by + * getFilter() will be included in the message body. The subject + * Formatter will see all LogRecord objects that were + * published regardless of the current Filter. The MIME type of the + * message body can be {@linkplain FileTypeMap#setDefaultFileTypeMap overridden} + * by adding a MIME {@linkplain MimetypesFileTypeMap entry} using the simple + * class name of the body formatter as the file extension. The MIME type of the + * attachments can be overridden by changing the attachment file name extension + * or by editing the default MIME entry for a specific file name extension. + * + *

    + * Attachments: + * This Handler allows multiple attachments per each email message. + * The presence of an attachment formatter will change the content type of the + * email message to a multi-part message. The attachment order maps directly to + * the array index order in this Handler with zero index being the + * first attachment. The number of attachment formatters controls the number of + * attachments per email and the content type of each attachment. The + * attachment filters determine if a LogRecord will be included in an + * attachment. If an attachment filter is null then all records are + * included for that attachment. Attachments without content will be omitted + * from email message. The attachment name formatters create the file name for + * an attachment. Custom attachment name formatters can be used to generate an + * attachment name based on the contents of the attachment. + * + *

    + * Push Level and Push Filter: + * The push method, push level, and optional push filter can be used to + * conditionally trigger a push at or prior to full capacity. When a push + * occurs, the current buffer is formatted into an email and is sent to the + * email server. If the push method, push level, or push filter trigger a push + * then the outgoing email is flagged as high importance with urgent priority. + * + *

    + * Buffering: + * Log records that are published are stored in an internal buffer. When this + * buffer reaches capacity the existing records are formatted and sent in an + * email. Any published records can be sent before reaching capacity by + * explictly calling the flush, push, or close + * methods. If a circular buffer is required then this handler can be wrapped + * with a {@linkplain java.util.logging.MemoryHandler} typically with an + * equivalent capacity, level, and push level. + * + *

    + * Error Handling: + * If the transport of an email message fails, the email is converted to + * a {@linkplain javax.mail.internet.MimeMessage#writeTo raw} + * {@linkplain java.io.ByteArrayOutputStream#toString(java.lang.String) string} + * and is then passed as the msg parameter to + * {@linkplain Handler#reportError reportError} along with the exception + * describing the cause of the failure. This allows custom error managers to + * store, {@linkplain javax.mail.internet.MimeMessage#MimeMessage( + * javax.mail.Session, java.io.InputStream) reconstruct}, and resend the + * original MimeMessage. The message parameter string is not a raw email + * if it starts with value returned from Level.SEVERE.getName(). + * Custom error managers can use the following test to determine if the + * msg parameter from this handler is a raw email: + * + *

    + * public void error(String msg, Exception ex, int code) {
    + *      if (msg == null || msg.length() == 0 || msg.startsWith(Level.SEVERE.getName())) {
    + *          super.error(msg, ex, code);
    + *      } else {
    + *          //The 'msg' parameter is a raw email.
    + *      }
    + * }
    + * 
    + * + * @author Jason Mehrens + * @since JavaMail 1.4.3 + */ +public class MailHandler extends Handler { + /** + * Use the emptyFilterArray method. + */ + private static final Filter[] EMPTY_FILTERS = new Filter[0]; + /** + * Use the emptyFormatterArray method. + */ + private static final Formatter[] EMPTY_FORMATTERS = new Formatter[0]; + /** + * Min byte size for header data. Used for initial arrays sizing. + */ + private static final int MIN_HEADER_SIZE = 1024; + /** + * Cache the off value. + */ + private static final int offValue = Level.OFF.intValue(); + /** + * The action to set the context class loader for use with the JavaMail API. + * Load and pin this before it is loaded in the close method. The field is + * declared as java.security.PrivilegedAction so + * WebappClassLoader.clearReferencesStaticFinal() method will ignore this + * field. + */ + private static final PrivilegedAction MAILHANDLER_LOADER + = new GetAndSetContext(MailHandler.class); + /** + * A thread local mutex used to prevent logging loops. This code has to be + * prepared to deal with unexpected null values since the + * WebappClassLoader.clearReferencesThreadLocals() and + * InnocuousThread.eraseThreadLocals() can remove thread local values. + * The MUTEX has 5 states: + * 1. A null value meaning default state of not publishing. + * 2. MUTEX_PUBLISH on first entry of a push or publish. + * 3. The index of the first filter to accept a log record. + * 4. MUTEX_REPORT when cycle of records is detected. + * 5. MUTEXT_LINKAGE when a linkage error is reported. + */ + private static final ThreadLocal MUTEX = new ThreadLocal<>(); + /** + * The marker object used to report a publishing state. + * This must be less than the body filter index (-1). + */ + private static final Integer MUTEX_PUBLISH = -2; + /** + * The used for the error reporting state. + * This must be less than the PUBLISH state. + */ + private static final Integer MUTEX_REPORT = -4; + /** + * The used for linkage error reporting. + * This must be less than the REPORT state. + */ + private static final Integer MUTEX_LINKAGE = -8; + /** + * Used to turn off security checks. + */ + private volatile boolean sealed; + /** + * Determines if we are inside of a push. + * Makes the handler properties read-only during a push. + */ + private boolean isWriting; + /** + * Holds all of the email server properties. + */ + private Properties mailProps; + /** + * Holds the authenticator required to login to the email server. + */ + private Authenticator auth; + /** + * Holds the session object used to generate emails. + * Sessions can be shared by multiple threads. + * See JDK-6228391 and K 6278. + */ + private Session session; + /** + * A mapping of log record to matching filter index. Negative one is used + * to track the body filter. Zero and greater is used to track the + * attachment parts. All indexes less than or equal to the matched value + * have already seen the given log record. + */ + private int[] matched; + /** + * Holds all of the log records that will be used to create the email. + */ + private LogRecord[] data; + /** + * The number of log records in the buffer. + */ + private int size; + /** + * The maximum number of log records to format per email. + * Used to roughly bound the size of an email. + * Every time the capacity is reached, the handler will push. + * The capacity will be negative if this handler is closed. + * Negative values are used to ensure all records are pushed. + */ + private int capacity; + /** + * Used to order all log records prior to formatting. The main email body + * and all attachments use the order determined by this comparator. If no + * comparator is present the log records will be in no specified order. + */ + private Comparator comparator; + /** + * Holds the formatter used to create the subject line of the email. + * A subject formatter is not required for the email message. + * All published records pass through the subject formatter. + */ + private Formatter subjectFormatter; + /** + * Holds the push level for this handler. + * This is only required if an email must be sent prior to shutdown + * or before the buffer is full. + */ + private Level pushLevel; + /** + * Holds the push filter for trigger conditions requiring an early push. + * Only gets called if the given log record is greater than or equal + * to the push level and the push level is not Level.OFF. + */ + private Filter pushFilter; + /** + * Holds the entry and body filter for this handler. + * There is no way to un-seal the super handler. + */ + private volatile Filter filter; + /** + * Holds the level for this handler. + * There is no way to un-seal the super handler. + */ + private volatile Level logLevel = Level.ALL; + /** + * Holds the filters for each attachment. Filters are optional for + * each attachment. This is declared volatile because this is treated as + * copy-on-write. The VO_VOLATILE_REFERENCE_TO_ARRAY warning is a false + * positive. + */ + @SuppressWarnings("VolatileArrayField") + private volatile Filter[] attachmentFilters; + /** + * Holds the encoding name for this handler. + * There is no way to un-seal the super handler. + */ + private String encoding; + /** + * Holds the entry and body filter for this handler. + * There is no way to un-seal the super handler. + */ + private Formatter formatter; + /** + * Holds the formatters that create the content for each attachment. + * Each formatter maps directly to an attachment. The formatters + * getHead, format, and getTail methods are only called if one or more + * log records pass through the attachment filters. + */ + private Formatter[] attachmentFormatters; + /** + * Holds the formatters that create the file name for each attachment. + * Each formatter must produce a non null and non empty name. + * The final file name will be the concatenation of one getHead call, plus + * all of the format calls, plus one getTail call. + */ + private Formatter[] attachmentNames; + /** + * Used to override the content type for the body and set the content type + * for each attachment. + */ + private FileTypeMap contentTypes; + /** + * Holds the error manager for this handler. + * There is no way to un-seal the super handler. + */ + private volatile ErrorManager errorManager = defaultErrorManager(); + + /** + * Creates a MailHandler that is configured by the + * LogManager configuration properties. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + */ + public MailHandler() { + init((Properties) null); + sealed = true; + checkAccess(); + } + + /** + * Creates a MailHandler that is configured by the + * LogManager configuration properties but overrides the + * LogManager capacity with the given capacity. + * @param capacity of the internal buffer. + * @throws IllegalArgumentException if capacity less than one. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + */ + public MailHandler(final int capacity) { + init((Properties) null); + sealed = true; + setCapacity0(capacity); + } + + /** + * Creates a mail handler with the given mail properties. + * The key/value pairs are defined in the Java Mail API + * documentation. This Handler will also search the + * LogManager for defaults if needed. + * @param props a non null properties object. + * @throws NullPointerException if props is null. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + */ + public MailHandler(final Properties props) { + if (props == null) { + throw new NullPointerException(); + } + init(props); + sealed = true; + setMailProperties0(props); + } + + /** + * Check if this Handler would actually log a given + * LogRecord into its internal buffer. + *

    + * This method checks if the LogRecord has an appropriate level and + * whether it satisfies any Filter including any attachment filters. + * However it does not check whether the LogRecord would + * result in a "push" of the buffer contents. + *

    + * @param record a LogRecord + * @return true if the LogRecord would be logged. + */ + @Override + public boolean isLoggable(final LogRecord record) { + int levelValue = getLevel().intValue(); + if (record.getLevel().intValue() < levelValue || levelValue == offValue) { + return false; + } + + Filter body = getFilter(); + if (body == null || body.isLoggable(record)) { + setMatchedPart(-1); + return true; + } + + return isAttachmentLoggable(record); + } + + /** + * Stores a LogRecord in the internal buffer. + *

    + * The isLoggable method is called to check if the given log record + * is loggable. If the given record is loggable, it is copied into + * an internal buffer. Then the record's level property is compared with + * the push level. If the given level of the LogRecord + * is greater than or equal to the push level then the push filter is + * called. If no push filter exists, the push filter returns true, + * or the capacity of the internal buffer has been reached then all buffered + * records are formatted into one email and sent to the server. + * + * @param record description of the log event. + */ + @Override + public void publish(final LogRecord record) { + /** + * It is possible for the handler to be closed after the + * call to isLoggable. In that case, the current thread + * will push to ensure that all published records are sent. + * See close(). + */ + + if (tryMutex()) { + try { + if (isLoggable(record)) { + record.getSourceMethodName(); //Infer caller. + publish0(record); + } + } catch (final LinkageError JDK8152515) { + reportLinkageError(JDK8152515, ErrorManager.WRITE_FAILURE); + } finally { + releaseMutex(); + } + } else { + reportUnPublishedError(record); + } + } + + /** + * Performs the publish after the record has been filtered. + * @param record the record. + * @since JavaMail 1.4.5 + */ + private void publish0(final LogRecord record) { + Message msg; + boolean priority; + synchronized (this) { + if (size == data.length && size < capacity) { + grow(); + } + + if (size < data.length) { + //assert data.length == matched.length; + matched[size] = getMatchedPart(); + data[size] = record; + ++size; //Be nice to client compiler. + priority = isPushable(record); + if (priority || size >= capacity) { + msg = writeLogRecords(ErrorManager.WRITE_FAILURE); + } else { + msg = null; + } + } else { + priority = false; + msg = null; + } + } + + if (msg != null) { + send(msg, priority, ErrorManager.WRITE_FAILURE); + } + } + + /** + * Report to the error manager that a logging loop was detected and + * we are going to break the cycle of messages. It is possible that + * a custom error manager could continue the cycle in which case + * we will stop trying to report errors. + * @param record the record or null. + * @since JavaMail 1.4.6 + */ + private void reportUnPublishedError(LogRecord record) { + final Integer idx = MUTEX.get(); + if (idx == null || idx > MUTEX_REPORT) { + MUTEX.set(MUTEX_REPORT); + try { + final String msg; + if (record != null) { + final Formatter f = createSimpleFormatter(); + msg = "Log record " + record.getSequenceNumber() + + " was not published. " + + head(f) + format(f, record) + tail(f, ""); + } else { + msg = null; + } + Exception e = new IllegalStateException( + "Recursive publish detected by thread " + + Thread.currentThread()); + reportError(msg, e, ErrorManager.WRITE_FAILURE); + } finally { + if (idx != null) { + MUTEX.set(idx); + } else { + MUTEX.remove(); + } + } + } + } + + /** + * Used to detect reentrance by the current thread to the publish method. + * This mutex is thread local scope and will not block other threads. + * The state is advanced on if the current thread is in a reset state. + * @return true if the mutex was acquired. + * @since JavaMail 1.4.6 + */ + private boolean tryMutex() { + if (MUTEX.get() == null) { + MUTEX.set(MUTEX_PUBLISH); + return true; + } else { + return false; + } + } + + /** + * Releases the mutex held by the current thread. + * This mutex is thread local scope and will not block other threads. + * @since JavaMail 1.4.6 + */ + private void releaseMutex() { + MUTEX.remove(); + } + + /** + * This is used to get the filter index from when {@code isLoggable} and + * {@code isAttachmentLoggable} was invoked by {@code publish} method. + * + * @return the filter index or MUTEX_PUBLISH if unknown. + * @since JavaMail 1.5.5 + * @throws NullPointerException if tryMutex was not called. + */ + private int getMatchedPart() { + //assert Thread.holdsLock(this); + Integer idx = MUTEX.get(); + if (idx == null || idx >= readOnlyAttachmentFilters().length) { + idx = MUTEX_PUBLISH; + } + return idx; + } + + /** + * This is used to record the filter index when {@code isLoggable} and + * {@code isAttachmentLoggable} was invoked by {@code publish} method. + * + * @param index the filter index. + * @since JavaMail 1.5.5 + */ + private void setMatchedPart(int index) { + if (MUTEX_PUBLISH.equals(MUTEX.get())) { + MUTEX.set(index); + } + } + + /** + * Clear previous matches when the filters are modified and there are + * existing log records that were matched. + * @param index the lowest filter index to clear. + * @since JavaMail 1.5.5 + */ + private void clearMatches(int index) { + assert Thread.holdsLock(this); + for (int r = 0; r < size; ++r) { + if (matched[r] >= index) { + matched[r] = MUTEX_PUBLISH; + } + } + } + + /** + * A callback method for when this object is about to be placed into + * commission. This contract is defined by the + * {@code org.glassfish.hk2.api.PostConstruct} interface. If this class is + * loaded via a lifecycle managed environment other than HK2 then it is + * recommended that this method is called either directly or through + * extending this class to signal that this object is ready for use. + * + * @since JavaMail 1.5.3 + */ + //@javax.annotation.PostConstruct + public void postConstruct() { + } + + /** + * A callback method for when this object is about to be decommissioned. + * This contract is defined by the {@code org.glassfish.hk2.api.PreDestory} + * interface. If this class is loaded via a lifecycle managed environment + * other than HK2 then it is recommended that this method is called either + * directly or through extending this class to signal that this object will + * be destroyed. + * + * @since JavaMail 1.5.3 + */ + //@javax.annotation.PreDestroy + public void preDestroy() { + /** + * Close can require permissions so just trigger a push. + */ + push(false, ErrorManager.CLOSE_FAILURE); + } + + /** + * Pushes any buffered records to the email server as high importance with + * urgent priority. The internal buffer is then cleared. Does nothing if + * called from inside a push. + * @see #flush() + */ + public void push() { + push(true, ErrorManager.FLUSH_FAILURE); + } + + /** + * Pushes any buffered records to the email server as normal priority. + * The internal buffer is then cleared. Does nothing if called from inside + * a push. + * @see #push() + */ + @Override + public void flush() { + push(false, ErrorManager.FLUSH_FAILURE); + } + + /** + * Prevents any other records from being published. + * Pushes any buffered records to the email server as normal priority. + * The internal buffer is then cleared. Once this handler is closed it + * will remain closed. + *

    + * If this Handler is only implicitly closed by the + * LogManager, then verification should be + * turned on. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @see #flush() + */ + @Override + public void close() { + try { + checkAccess(); //Ensure setLevel works before clearing the buffer. + Message msg = null; + synchronized (this) { + try { + msg = writeLogRecords(ErrorManager.CLOSE_FAILURE); + } finally { //Change level after formatting. + this.logLevel = Level.OFF; + /** + * The sign bit of the capacity is set to ensure that + * records that have passed isLoggable, but have yet to be + * added to the internal buffer, are immediately pushed as + * an email. + */ + if (this.capacity > 0) { + this.capacity = -this.capacity; + } + + //Ensure not inside a push. + if (size == 0 && data.length != 1) { + this.data = new LogRecord[1]; + this.matched = new int[this.data.length]; + } + } + } + + if (msg != null) { + send(msg, false, ErrorManager.CLOSE_FAILURE); + } + } catch (final LinkageError JDK8152515) { + reportLinkageError(JDK8152515, ErrorManager.CLOSE_FAILURE); + } + } + + /** + * Set the log level specifying which message levels will be + * logged by this Handler. Message levels lower than this + * value will be discarded. + * @param newLevel the new value for the log level + * @throws NullPointerException if newLevel is null. + * @throws SecurityException if a security manager exists and + * the caller does not have LoggingPermission("control"). + */ + @Override + public void setLevel(final Level newLevel) { + if (newLevel == null) { + throw new NullPointerException(); + } + checkAccess(); + + //Don't allow a closed handler to be opened (half way). + synchronized (this) { //Wait for writeLogRecords. + if (this.capacity > 0) { + this.logLevel = newLevel; + } + } + } + + /** + * Get the log level specifying which messages will be logged by this + * Handler. Message levels lower than this level will be + * discarded. + * + * @return the level of messages being logged. + */ + @Override + public Level getLevel() { + return logLevel; //Volatile access. + } + + /** + * Retrieves the ErrorManager for this Handler. + * + * @return the ErrorManager for this Handler + * @throws SecurityException if a security manager exists and if the caller + * does not have LoggingPermission("control"). + */ + @Override + public ErrorManager getErrorManager() { + checkAccess(); + return this.errorManager; //Volatile access. + } + + /** + * Define an ErrorManager for this Handler. + *

    + * The ErrorManager's "error" method will be invoked if any errors occur + * while using this Handler. + * + * @param em the new ErrorManager + * @throws SecurityException if a security manager exists and if the + * caller does not have LoggingPermission("control"). + * @throws NullPointerException if the given error manager is null. + */ + @Override + public void setErrorManager(final ErrorManager em) { + checkAccess(); + setErrorManager0(em); + } + + /** + * Sets the error manager on this handler and the super handler. In secure + * environments the super call may not be allowed which is not a failure + * condition as it is an attempt to free the unused handler error manager. + * + * @param em a non null error manager. + * @throws NullPointerException if the given error manager is null. + * @since JavaMail 1.5.6 + */ + private void setErrorManager0(final ErrorManager em) { + if (em == null) { + throw new NullPointerException(); + } + try { + synchronized (this) { //Wait for writeLogRecords. + this.errorManager = em; + super.setErrorManager(em); //Try to free super error manager. + } + } catch (RuntimeException | LinkageError ignore) { + } + } + + /** + * Get the current Filter for this Handler. + * + * @return a Filter object (may be null) + */ + @Override + public Filter getFilter() { + return this.filter; //Volatile access. + } + + /** + * Set a Filter to control output on this Handler. + *

    + * For each call of publish the Handler will call this + * Filter (if it is non-null) to check if the LogRecord + * should be published or discarded. + * + * @param newFilter a Filter object (may be null) + * @throws SecurityException if a security manager exists and if the caller + * does not have LoggingPermission("control"). + */ + @Override + public void setFilter(final Filter newFilter) { + checkAccess(); + synchronized (this) { //Wait for writeLogRecords. + if (newFilter != filter) { + clearMatches(-1); + } + this.filter = newFilter; //Volatile access. + } + } + + /** + * Return the character encoding for this Handler. + * + * @return The encoding name. May be null, which indicates the default + * encoding should be used. + */ + @Override + public synchronized String getEncoding() { + return this.encoding; + } + + /** + * Set the character encoding used by this Handler. + *

    + * The encoding should be set before any LogRecords are written + * to the Handler. + * + * @param encoding The name of a supported character encoding. May be + * null, to indicate the default platform encoding. + * @throws SecurityException if a security manager exists and if the caller + * does not have LoggingPermission("control"). + * @throws UnsupportedEncodingException if the named encoding is not + * supported. + */ + @Override + public void setEncoding(String encoding) throws UnsupportedEncodingException { + checkAccess(); + setEncoding0(encoding); + } + + /** + * Set the character encoding used by this handler. This method does not + * check permissions of the caller. + * + * @param e any encoding name or null for the default. + * @throws UnsupportedEncodingException if the given encoding is not supported. + */ + private void setEncoding0(String e) throws UnsupportedEncodingException { + if (e != null) { + try { + if (!java.nio.charset.Charset.isSupported(e)) { + throw new UnsupportedEncodingException(e); + } + } catch (java.nio.charset.IllegalCharsetNameException icne) { + throw new UnsupportedEncodingException(e); + } + } + + synchronized (this) { //Wait for writeLogRecords. + this.encoding = e; + } + } + + /** + * Return the Formatter for this Handler. + * + * @return the Formatter (may be null). + */ + @Override + public synchronized Formatter getFormatter() { + return this.formatter; + } + + /** + * Set a Formatter. This Formatter will be used to format + * LogRecords for this Handler. + *

    + * Some Handlers may not use Formatters, in which case the + * Formatter will be remembered, but not used. + *

    + * @param newFormatter the Formatter to use (may not be null) + * @throws SecurityException if a security manager exists and if the caller + * does not have LoggingPermission("control"). + * @throws NullPointerException if the given formatter is null. + */ + @Override + public synchronized void setFormatter(Formatter newFormatter) throws SecurityException { + checkAccess(); + if (newFormatter == null) { + throw new NullPointerException(); + } + this.formatter = newFormatter; + } + + /** + * Gets the push level. The default is Level.OFF meaning that + * this Handler will only push when the internal buffer is full. + * @return the push level. + */ + public final synchronized Level getPushLevel() { + return this.pushLevel; + } + + /** + * Sets the push level. This level is used to trigger a push so that + * all pending records are formatted and sent to the email server. When + * the push level triggers a send, the resulting email is flagged as + * high importance with urgent priority. + * @param level Level object. + * @throws NullPointerException if level is null. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws IllegalStateException if called from inside a push. + */ + public final synchronized void setPushLevel(final Level level) { + checkAccess(); + if (level == null) { + throw new NullPointerException(); + } + + if (isWriting) { + throw new IllegalStateException(); + } + this.pushLevel = level; + } + + /** + * Gets the push filter. The default is null. + * @return the push filter or null. + */ + public final synchronized Filter getPushFilter() { + return this.pushFilter; + } + + /** + * Sets the push filter. This filter is only called if the given + * LogRecord level was greater than the push level. If this + * filter returns true, all pending records are formatted and sent + * to the email server. When the push filter triggers a send, the resulting + * email is flagged as high importance with urgent priority. + * @param filter push filter or null + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws IllegalStateException if called from inside a push. + */ + public final synchronized void setPushFilter(final Filter filter) { + checkAccess(); + if (isWriting) { + throw new IllegalStateException(); + } + this.pushFilter = filter; + } + + /** + * Gets the comparator used to order all LogRecord objects prior + * to formatting. If null then the order is unspecified. + * @return the LogRecord comparator. + */ + public final synchronized Comparator getComparator() { + return this.comparator; + } + + /** + * Sets the comparator used to order all LogRecord objects prior + * to formatting. If null then the order is unspecified. + * @param c the LogRecord comparator. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws IllegalStateException if called from inside a push. + */ + public final synchronized void setComparator(Comparator c) { + checkAccess(); + if (isWriting) { + throw new IllegalStateException(); + } + this.comparator = c; + } + + /** + * Gets the number of log records the internal buffer can hold. When + * capacity is reached, Handler will format all LogRecord + * objects into one email message. + * @return the capacity. + */ + public final synchronized int getCapacity() { + assert capacity != Integer.MIN_VALUE && capacity != 0 : capacity; + return Math.abs(capacity); + } + + /** + * Gets the Authenticator used to login to the email server. + * @return an Authenticator or null if none is required. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + */ + public final synchronized Authenticator getAuthenticator() { + checkAccess(); + return this.auth; + } + + /** + * Sets the Authenticator used to login to the email server. + * @param auth an Authenticator object or null if none is required. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws IllegalStateException if called from inside a push. + */ + public final void setAuthenticator(final Authenticator auth) { + this.setAuthenticator0(auth); + } + + /** + * Sets the Authenticator used to login to the email server. + * @param password a password, empty array can be used to only supply a + * user name set by mail.user property, or null if no credentials + * are required. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws IllegalStateException if called from inside a push. + * @see String#toCharArray() + * @since JavaMail 1.4.6 + */ + public final void setAuthenticator(final char... password) { + if (password == null) { + setAuthenticator0((Authenticator) null); + } else { + setAuthenticator0(DefaultAuthenticator.of(new String(password))); + } + } + + /** + * A private hook to handle possible future overrides. See public method. + * @param auth see public method. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws IllegalStateException if called from inside a push. + */ + private void setAuthenticator0(final Authenticator auth) { + checkAccess(); + + Session settings; + synchronized (this) { + if (isWriting) { + throw new IllegalStateException(); + } + this.auth = auth; + settings = updateSession(); + } + verifySettings(settings); + } + + /** + * Sets the mail properties used for the session. The key/value pairs + * are defined in the Java Mail API documentation. This + * Handler will also search the LogManager for defaults + * if needed. + * @param props a non null properties object. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws NullPointerException if props is null. + * @throws IllegalStateException if called from inside a push. + */ + public final void setMailProperties(Properties props) { + this.setMailProperties0(props); + } + + /** + * A private hook to handle overrides when the public method is declared + * non final. See public method for details. + * @param props see public method. + */ + private void setMailProperties0(Properties props) { + checkAccess(); + props = (Properties) props.clone(); //Allow subclass. + Session settings; + synchronized (this) { + if (isWriting) { + throw new IllegalStateException(); + } + this.mailProps = props; + settings = updateSession(); + } + verifySettings(settings); + } + + /** + * Gets a copy of the mail properties used for the session. + * @return a non null properties object. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + */ + public final Properties getMailProperties() { + checkAccess(); + final Properties props; + synchronized (this) { + props = this.mailProps; + } + return (Properties) props.clone(); + } + + /** + * Gets the attachment filters. If the attachment filter does not + * allow any LogRecord to be formatted, the attachment may + * be omitted from the email. + * @return a non null array of attachment filters. + */ + public final Filter[] getAttachmentFilters() { + return readOnlyAttachmentFilters().clone(); + } + + /** + * Sets the attachment filters. + * @param filters a non null array of filters. A null + * index value is allowed. A null value means that all + * records are allowed for the attachment at that index. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws NullPointerException if filters is null + * @throws IndexOutOfBoundsException if the number of attachment + * name formatters do not match the number of attachment formatters. + * @throws IllegalStateException if called from inside a push. + */ + public final void setAttachmentFilters(Filter... filters) { + checkAccess(); + if (filters.length == 0) { + filters = emptyFilterArray(); + } else { + filters = Arrays.copyOf(filters, filters.length, Filter[].class); + } + synchronized (this) { + if (this.attachmentFormatters.length != filters.length) { + throw attachmentMismatch(this.attachmentFormatters.length, filters.length); + } + + if (isWriting) { + throw new IllegalStateException(); + } + + if (size != 0) { + for (int i = 0; i < filters.length; ++i) { + if (filters[i] != attachmentFilters[i]) { + clearMatches(i); + break; + } + } + } + this.attachmentFilters = filters; + } + } + + /** + * Gets the attachment formatters. This Handler is using + * attachments only if the returned array length is non zero. + * @return a non null array of formatters. + */ + public final Formatter[] getAttachmentFormatters() { + Formatter[] formatters; + synchronized (this) { + formatters = this.attachmentFormatters; + } + return formatters.clone(); + } + + /** + * Sets the attachment Formatter object for this handler. + * The number of formatters determines the number of attachments per + * email. This method should be the first attachment method called. + * To remove all attachments, call this method with empty array. + * @param formatters a non null array of formatters. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws NullPointerException if the given array or any array index is + * null. + * @throws IllegalStateException if called from inside a push. + */ + public final void setAttachmentFormatters(Formatter... formatters) { + checkAccess(); + if (formatters.length == 0) { //Null check and length check. + formatters = emptyFormatterArray(); + } else { + formatters = Arrays.copyOf(formatters, + formatters.length, Formatter[].class); + for (int i = 0; i < formatters.length; ++i) { + if (formatters[i] == null) { + throw new NullPointerException(atIndexMsg(i)); + } + } + } + + synchronized (this) { + if (isWriting) { + throw new IllegalStateException(); + } + + this.attachmentFormatters = formatters; + this.alignAttachmentFilters(); + this.alignAttachmentNames(); + } + } + + /** + * Gets the attachment name formatters. + * If the attachment names were set using explicit names then + * the names can be returned by calling toString on each + * attachment name formatter. + * @return non null array of attachment name formatters. + */ + public final Formatter[] getAttachmentNames() { + final Formatter[] formatters; + synchronized (this) { + formatters = this.attachmentNames; + } + return formatters.clone(); + } + + /** + * Sets the attachment file name for each attachment. All control + * characters are removed from the attachment names. + * This method will create a set of custom formatters. + * @param names an array of names. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws IndexOutOfBoundsException if the number of attachment + * names do not match the number of attachment formatters. + * @throws IllegalArgumentException if any name is empty. + * @throws NullPointerException if any given array or name is null. + * @throws IllegalStateException if called from inside a push. + * @see Character#isISOControl(char) + * @see Character#isISOControl(int) + */ + public final void setAttachmentNames(final String... names) { + checkAccess(); + + final Formatter[] formatters; + if (names.length == 0) { + formatters = emptyFormatterArray(); + } else { + formatters = new Formatter[names.length]; + } + + for (int i = 0; i < names.length; ++i) { + final String name = names[i]; + if (name != null) { + if (name.length() > 0) { + formatters[i] = TailNameFormatter.of(name); + } else { + throw new IllegalArgumentException(atIndexMsg(i)); + } + } else { + throw new NullPointerException(atIndexMsg(i)); + } + } + + synchronized (this) { + if (this.attachmentFormatters.length != names.length) { + throw attachmentMismatch(this.attachmentFormatters.length, names.length); + } + + if (isWriting) { + throw new IllegalStateException(); + } + this.attachmentNames = formatters; + } + } + + /** + * Sets the attachment file name formatters. The format method of each + * attachment formatter will see only the LogRecord objects that + * passed its attachment filter during formatting. The format method will + * typically return an empty string. Instead of being used to format + * records, it is used to gather information about the contents of an + * attachment. The getTail method should be used to construct the + * attachment file name and reset any formatter collected state. All + * control characters will be removed from the output of the formatter. The + * toString method of the given formatter should be overridden to + * provide a useful attachment file name, if possible. + * @param formatters and array of attachment name formatters. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws IndexOutOfBoundsException if the number of attachment + * name formatters do not match the number of attachment formatters. + * @throws NullPointerException if any given array or name is null. + * @throws IllegalStateException if called from inside a push. + * @see Character#isISOControl(char) + * @see Character#isISOControl(int) + */ + public final void setAttachmentNames(Formatter... formatters) { + checkAccess(); + + if (formatters.length == 0) { + formatters = emptyFormatterArray(); + } else { + formatters = Arrays.copyOf(formatters, formatters.length, + Formatter[].class); + } + + for (int i = 0; i < formatters.length; ++i) { + if (formatters[i] == null) { + throw new NullPointerException(atIndexMsg(i)); + } + } + + synchronized (this) { + if (this.attachmentFormatters.length != formatters.length) { + throw attachmentMismatch(this.attachmentFormatters.length, + formatters.length); + } + + if (isWriting) { + throw new IllegalStateException(); + } + + this.attachmentNames = formatters; + } + } + + /** + * Gets the formatter used to create the subject line. + * If the subject was created using a literal string then + * the toString method can be used to get the subject line. + * @return the formatter. + */ + public final synchronized Formatter getSubject() { + return this.subjectFormatter; + } + + /** + * Sets a literal string for the email subject. All control characters are + * removed from the subject line. + * @param subject a non null string. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws NullPointerException if subject is null. + * @throws IllegalStateException if called from inside a push. + * @see Character#isISOControl(char) + * @see Character#isISOControl(int) + */ + public final void setSubject(final String subject) { + if (subject != null) { + this.setSubject(TailNameFormatter.of(subject)); + } else { + checkAccess(); + throw new NullPointerException(); + } + } + + /** + * Sets the subject formatter for email. The format method of the subject + * formatter will see all LogRecord objects that were published to + * this Handler during formatting and will typically return an + * empty string. This formatter is used to gather information to create a + * summary about what information is contained in the email. The + * getTail method should be used to construct the subject and reset + * any formatter collected state. All control characters + * will be removed from the formatter output. The toString + * method of the given formatter should be overridden to provide a useful + * subject, if possible. + * @param format the subject formatter. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws NullPointerException if format is null. + * @throws IllegalStateException if called from inside a push. + * @see Character#isISOControl(char) + * @see Character#isISOControl(int) + */ + public final void setSubject(final Formatter format) { + checkAccess(); + if (format == null) { + throw new NullPointerException(); + } + + synchronized (this) { + if (isWriting) { + throw new IllegalStateException(); + } + this.subjectFormatter = format; + } + } + + /** + * Protected convenience method to report an error to this Handler's + * ErrorManager. This method will prefix all non null error messages with + * Level.SEVERE.getName(). This allows the receiving error + * manager to determine if the msg parameter is a simple error + * message or a raw email message. + * @param msg a descriptive string (may be null) + * @param ex an exception (may be null) + * @param code an error code defined in ErrorManager + */ + @Override + protected void reportError(String msg, Exception ex, int code) { + try { + if (msg != null) { + errorManager.error(Level.SEVERE.getName() + .concat(": ").concat(msg), ex, code); + } else { + errorManager.error(null, ex, code); + } + } catch (RuntimeException | LinkageError GLASSFISH_21258) { + reportLinkageError(GLASSFISH_21258, code); + } + } + + /** + * Calls log manager checkAccess if this is sealed. + */ + private void checkAccess() { + if (sealed) { + LogManagerProperties.checkLogManagerAccess(); + } + } + + /** + * Determines the mimeType of a formatter from the getHead call. + * This could be made protected, or a new class could be created to do + * this type of conversion. Currently, this is only used for the body + * since the attachments are computed by filename. + * Package-private for unit testing. + * @param chunk any char sequence or null. + * @return return the mime type or null for text/plain. + */ + final String contentTypeOf(CharSequence chunk) { + if (!isEmpty(chunk)) { + final int MAX_CHARS = 25; + if (chunk.length() > MAX_CHARS) { + chunk = chunk.subSequence(0, MAX_CHARS); + } + try { + final String charset = getEncodingName(); + final byte[] b = chunk.toString().getBytes(charset); + final ByteArrayInputStream in = new ByteArrayInputStream(b); + assert in.markSupported() : in.getClass().getName(); + return URLConnection.guessContentTypeFromStream(in); + } catch (final IOException IOE) { + reportError(IOE.getMessage(), IOE, ErrorManager.FORMAT_FAILURE); + } + } + return null; //text/plain + } + + /** + * Determines the mimeType of a formatter by the class name. This method + * avoids calling getHead and getTail of content formatters during verify + * because they might trigger side effects or excessive work. The name + * formatters and subject are usually safe to call. + * Package-private for unit testing. + * + * @param f the formatter or null. + * @return return the mime type or null, meaning text/plain. + * @since JavaMail 1.5.6 + */ + final String contentTypeOf(final Formatter f) { + assert Thread.holdsLock(this); + if (f != null) { + String type = getContentType(f.getClass().getName()); + if (type != null) { + return type; + } + + for (Class k = f.getClass(); k != Formatter.class; + k = k.getSuperclass()) { + String name; + try { + name = k.getSimpleName(); + } catch (final InternalError JDK8057919) { + name = k.getName(); + } + name = name.toLowerCase(Locale.ENGLISH); + for (int idx = name.indexOf('$') + 1; + (idx = name.indexOf("ml", idx)) > -1; idx += 2) { + if (idx > 0) { + if (name.charAt(idx - 1) == 'x') { + return "application/xml"; + } + if (idx > 1 && name.charAt(idx - 2) == 'h' + && name.charAt(idx - 1) == 't') { + return "text/html"; + } + } + } + } + } + return null; + } + + /** + * Determines if the given throwable is a no content exception. It is + * assumed Transport.sendMessage will call Message.writeTo so we need to + * ignore any exceptions that could be layered on top of that call chain to + * infer that sendMessage is failing because of writeTo. Package-private + * for unit testing. + * @param msg the message without content. + * @param t the throwable chain to test. + * @return true if the throwable is a missing content exception. + * @throws NullPointerException if any of the arguments are null. + * @since JavaMail 1.4.5 + */ + @SuppressWarnings({"UseSpecificCatch", "ThrowableResultIgnored"}) + final boolean isMissingContent(Message msg, Throwable t) { + final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER); + try { + msg.writeTo(new ByteArrayOutputStream(MIN_HEADER_SIZE)); + } catch (final RuntimeException RE) { + throw RE; //Avoid catch all. + } catch (final Exception noContent) { + final String txt = noContent.getMessage(); + if (!isEmpty(txt)) { + int limit = 0; + while (t != null) { + if (noContent.getClass() == t.getClass() + && txt.equals(t.getMessage())) { + return true; + } + + //Not all JavaMail implementations support JDK 1.4 exception + //chaining. + final Throwable cause = t.getCause(); + if (cause == null && t instanceof MessagingException) { + t = ((MessagingException) t).getNextException(); + } else { + t = cause; + } + + //Deal with excessive cause chains and cyclic throwables. + if (++limit == (1 << 16)) { + break; //Give up. + } + } + } + } finally { + getAndSetContextClassLoader(ccl); + } + return false; + } + + /** + * Converts a mime message to a raw string or formats the reason + * why message can't be changed to raw string and reports it. + * @param msg the mime message. + * @param ex the original exception. + * @param code the ErrorManager code. + * @since JavaMail 1.4.5 + */ + @SuppressWarnings("UseSpecificCatch") + private void reportError(Message msg, Exception ex, int code) { + try { + try { //Use direct call so we do not prefix raw email. + errorManager.error(toRawString(msg), ex, code); + } catch (final RuntimeException re) { + reportError(toMsgString(re), ex, code); + } catch (final Exception e) { + reportError(toMsgString(e), ex, code); + } + } catch (final LinkageError GLASSFISH_21258) { + reportLinkageError(GLASSFISH_21258, code); + } + } + + /** + * Reports the given linkage error or runtime exception. + * + * The current LogManager code will stop closing all remaining handlers if + * an error is thrown during resetLogger. This is a workaround for + * GLASSFISH-21258 and JDK-8152515. + * @param le the linkage error or a RuntimeException. + * @param code the ErrorManager code. + * @throws NullPointerException if error is null. + * @since JavaMail 1.5.3 + */ + private void reportLinkageError(final Throwable le, final int code) { + if (le == null) { + throw new NullPointerException(String.valueOf(code)); + } + + final Integer idx = MUTEX.get(); + if (idx == null || idx > MUTEX_LINKAGE) { + MUTEX.set(MUTEX_LINKAGE); + try { + Thread.currentThread().getUncaughtExceptionHandler() + .uncaughtException(Thread.currentThread(), le); + } catch (RuntimeException | LinkageError ignore) { + } finally { + if (idx != null) { + MUTEX.set(idx); + } else { + MUTEX.remove(); + } + } + } + } + + /** + * Determines the mimeType from the given file name. + * Used to override the body content type and used for all attachments. + * @param name the file name or class name. + * @return the mime type or null for text/plain. + */ + private String getContentType(final String name) { + assert Thread.holdsLock(this); + final String type = contentTypes.getContentType(name); + if ("application/octet-stream".equalsIgnoreCase(type)) { + return null; //Formatters return strings, default to text/plain. + } + return type; + } + + /** + * Gets the encoding set for this handler, mime encoding, or file encoding. + * @return the java charset name, never null. + * @since JavaMail 1.4.5 + */ + private String getEncodingName() { + String charset = getEncoding(); + if (charset == null) { + charset = MimeUtility.getDefaultJavaCharset(); + } + return charset; + } + + /** + * Set the content for a part using the encoding assigned to the handler. + * @param part the part to assign. + * @param buf the formatted data. + * @param type the mime type or null, meaning text/plain. + * @throws MessagingException if there is a problem. + */ + private void setContent(MimePart part, CharSequence buf, String type) throws MessagingException { + final String charset = getEncodingName(); + if (type != null && !"text/plain".equalsIgnoreCase(type)) { + type = contentWithEncoding(type, charset); + try { + DataSource source = new ByteArrayDataSource(buf.toString(), type); + part.setDataHandler(new DataHandler(source)); + } catch (final IOException IOE) { + reportError(IOE.getMessage(), IOE, ErrorManager.FORMAT_FAILURE); + part.setText(buf.toString(), charset); + } + } else { + part.setText(buf.toString(), MimeUtility.mimeCharset(charset)); + } + } + + /** + * Replaces the charset parameter with the current encoding. + * @param type the content type. + * @param encoding the java charset name. + * @return the type with a specified encoding. + */ + private String contentWithEncoding(String type, String encoding) { + assert encoding != null; + try { + final ContentType ct = new ContentType(type); + ct.setParameter("charset", MimeUtility.mimeCharset(encoding)); + encoding = ct.toString(); //See javax.mail.internet.ContentType. + if (!isEmpty(encoding)) { //Support pre K5687. + type = encoding; + } + } catch (final MessagingException ME) { + reportError(type, ME, ErrorManager.FORMAT_FAILURE); + } + return type; + } + + /** + * Sets the capacity for this handler. This method is kept private + * because we would have to define a public policy for when the size is + * greater than the capacity. + * E.G. do nothing, flush now, truncate now, push now and resize. + * @param newCapacity the max number of records. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + * @throws IllegalStateException if called from inside a push. + */ + private synchronized void setCapacity0(final int newCapacity) { + checkAccess(); + if (newCapacity <= 0) { + throw new IllegalArgumentException("Capacity must be greater than zero."); + } + + if (isWriting) { + throw new IllegalStateException(); + } + + if (this.capacity < 0) { //If closed, remain closed. + this.capacity = -newCapacity; + } else { + this.capacity = newCapacity; + } + } + + /** + * Gets the attachment filters using a happens-before relationship between + * this method and setAttachmentFilters. The attachment filters are treated + * as copy-on-write, so the returned array must never be modified or + * published outside this class. + * @return a read only array of filters. + */ + private Filter[] readOnlyAttachmentFilters() { + return this.attachmentFilters; + } + + /** + * Factory for empty formatter arrays. + * @return an empty array. + */ + private static Formatter[] emptyFormatterArray() { + return EMPTY_FORMATTERS; + } + + /** + * Factory for empty filter arrays. + * @return an empty array. + */ + private static Filter[] emptyFilterArray() { + return EMPTY_FILTERS; + } + + /** + * Expand or shrink the attachment name formatters with the attachment + * formatters. + * @return true if size was changed. + */ + private boolean alignAttachmentNames() { + assert Thread.holdsLock(this); + boolean fixed = false; + final int expect = this.attachmentFormatters.length; + final int current = this.attachmentNames.length; + if (current != expect) { + this.attachmentNames = Arrays.copyOf(attachmentNames, expect, + Formatter[].class); + fixed = current != 0; + } + + //Copy of zero length array is cheap, warm up copyOf. + if (expect == 0) { + this.attachmentNames = emptyFormatterArray(); + assert this.attachmentNames.length == 0; + } else { + for (int i = 0; i < expect; ++i) { + if (this.attachmentNames[i] == null) { + this.attachmentNames[i] = TailNameFormatter.of( + toString(this.attachmentFormatters[i])); + } + } + } + return fixed; + } + + /** + * Expand or shrink the attachment filters with the attachment formatters. + * @return true if the size was changed. + */ + private boolean alignAttachmentFilters() { + assert Thread.holdsLock(this); + + boolean fixed = false; + final int expect = this.attachmentFormatters.length; + final int current = this.attachmentFilters.length; + if (current != expect) { + this.attachmentFilters = Arrays.copyOf(attachmentFilters, expect, + Filter[].class); + clearMatches(current); + fixed = current != 0; + + //Array elements default to null so skip filling if body filter + //is null. If not null then only assign to expanded elements. + final Filter body = this.filter; + if (body != null) { + for (int i = current; i < expect; ++i) { + this.attachmentFilters[i] = body; + } + } + } + + //Copy of zero length array is cheap, warm up copyOf. + if (expect == 0) { + this.attachmentFilters = emptyFilterArray(); + assert this.attachmentFilters.length == 0; + } + return fixed; + } + + /** + * Sets the size to zero and clears the current buffer. + */ + private void reset() { + assert Thread.holdsLock(this); + if (size < data.length) { + Arrays.fill(data, 0, size, null); + } else { + Arrays.fill(data, null); + } + this.size = 0; + } + + /** + * Expands the internal buffer up to the capacity. + */ + private void grow() { + assert Thread.holdsLock(this); + final int len = data.length; + int newCapacity = len + (len >> 1) + 1; + if (newCapacity > capacity || newCapacity < len) { + newCapacity = capacity; + } + assert len != capacity : len; + this.data = Arrays.copyOf(data, newCapacity, LogRecord[].class); + this.matched = Arrays.copyOf(matched, newCapacity); + } + + /** + * Configures the handler properties from the log manager. + * @param props the given mail properties. Maybe null and are never + * captured by this handler. + * @throws SecurityException if a security manager exists and the + * caller does not have LoggingPermission("control"). + */ + private synchronized void init(final Properties props) { + assert this.errorManager != null; + final String p = getClass().getName(); + this.mailProps = new Properties(); //See method param comments. + final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER); + try { + this.contentTypes = FileTypeMap.getDefaultFileTypeMap(); + } finally { + getAndSetContextClassLoader(ccl); + } + + //Assign any custom error manager first so it can detect all failures. + initErrorManager(p); + + initLevel(p); + initFilter(p); + initCapacity(p); + initAuthenticator(p); + + initEncoding(p); + initFormatter(p); + initComparator(p); + initPushLevel(p); + initPushFilter(p); + + initSubject(p); + + initAttachmentFormaters(p); + initAttachmentFilters(p); + initAttachmentNames(p); + + if (props == null && fromLogManager(p.concat(".verify")) != null) { + verifySettings(initSession()); + } + intern(); //Show verify warnings first. + } + + /** + * Interns the error manager, formatters, and filters contained in this + * handler. The comparator is not interned. This method can only be + * called from init after all of formatters and filters are in a constructed + * and in a consistent state. + * @since JavaMail 1.5.0 + */ + private void intern() { + assert Thread.holdsLock(this); + try { + Object canidate; + Object result; + final Map seen = new HashMap<>(); + try { + intern(seen, this.errorManager); + } catch (final SecurityException se) { + reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE); + } + + try { + canidate = this.filter; + result = intern(seen, canidate); + if (result != canidate && result instanceof Filter) { + this.filter = (Filter) result; + } + + canidate = this.formatter; + result = intern(seen, canidate); + if (result != canidate && result instanceof Formatter) { + this.formatter = (Formatter) result; + } + } catch (final SecurityException se) { + reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE); + } + + canidate = this.subjectFormatter; + result = intern(seen, canidate); + if (result != canidate && result instanceof Formatter) { + this.subjectFormatter = (Formatter) result; + } + + canidate = this.pushFilter; + result = intern(seen, canidate); + if (result != canidate && result instanceof Filter) { + this.pushFilter = (Filter) result; + } + + for (int i = 0; i < attachmentFormatters.length; ++i) { + canidate = attachmentFormatters[i]; + result = intern(seen, canidate); + if (result != canidate && result instanceof Formatter) { + attachmentFormatters[i] = (Formatter) result; + } + + canidate = attachmentFilters[i]; + result = intern(seen, canidate); + if (result != canidate && result instanceof Filter) { + attachmentFilters[i] = (Filter) result; + } + + canidate = attachmentNames[i]; + result = intern(seen, canidate); + if (result != canidate && result instanceof Formatter) { + attachmentNames[i] = (Formatter) result; + } + } + } catch (final Exception skip) { + reportError(skip.getMessage(), skip, ErrorManager.OPEN_FAILURE); + } catch (final LinkageError skip) { + reportError(skip.getMessage(), new InvocationTargetException(skip), + ErrorManager.OPEN_FAILURE); + } + } + + /** + * If possible performs an intern of the given object into the + * map. If the object can not be interned the given object is returned. + * @param m the map used to record the interned values. + * @param o the object to try an intern. + * @return the original object or an intern replacement. + * @throws SecurityException if this operation is not allowed by the + * security manager. + * @throws Exception if there is an unexpected problem. + * @since JavaMail 1.5.0 + */ + private Object intern(Map m, Object o) throws Exception { + if (o == null) { + return null; + } + + /** + * The common case is that most objects will not intern. The given + * object has a public no argument constructor or is an instance of a + * TailNameFormatter. TailNameFormatter is safe use as a map key. + * For everything else we create a clone of the given object. + * This is done because of the following: + * 1. Clones can be used to test that a class provides an equals method + * and that the equals method works correctly. + * 2. Calling equals on the given object is assumed to be cheap. + * 3. The intern map can be filtered so it only contains objects that + * can be interned, which reduces the memory footprint. + * 4. Clones are method local garbage. + * 5. Hash code is only called on the clones so bias locking is not + * disabled on the objects the handler will use. + */ + final Object key; + if (o.getClass().getName().equals(TailNameFormatter.class.getName())) { + key = o; + } else { + //This call was already made in the LogManagerProperties so this + //shouldn't trigger loading of any lazy reflection code. + key = o.getClass().getConstructor().newInstance(); + } + + final Object use; + //Check the classloaders of each object avoiding the security manager. + if (key.getClass() == o.getClass()) { + Object found = m.get(key); //Transitive equals test. + if (found == null) { + //Ensure that equals is symmetric to prove intern is safe. + final boolean right = key.equals(o); + final boolean left = o.equals(key); + if (right && left) { + //Assume hashCode is defined at this point. + found = m.put(o, o); + if (found != null) { + reportNonDiscriminating(key, found); + found = m.remove(key); + if (found != o) { + reportNonDiscriminating(key, found); + m.clear(); //Try to restore order. + } + } + } else { + if (right != left) { + reportNonSymmetric(o, key); + } + } + use = o; + } else { + //Check for a discriminating equals method. + if (o.getClass() == found.getClass()) { + use = found; + } else { + reportNonDiscriminating(o, found); + use = o; + } + } + } else { + use = o; + } + return use; + } + + /** + * Factory method used to create a java.util.logging.SimpleFormatter. + * @return a new SimpleFormatter. + * @since JavaMail 1.5.6 + */ + private static Formatter createSimpleFormatter() { + //Don't force the byte code verifier to load the formatter. + return Formatter.class.cast(new SimpleFormatter()); + } + + /** + * Checks a char sequence value for null or empty. + * @param s the char sequence. + * @return true if the given string is null or zero length. + */ + private static boolean isEmpty(final CharSequence s) { + return s == null || s.length() == 0; + } + + /** + * Checks that a string is not empty and not equal to the literal "null". + * @param name the string to check for a value. + * @return true if the string has a valid value. + */ + private static boolean hasValue(final String name) { + return !isEmpty(name) && !"null".equalsIgnoreCase(name); + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initAttachmentFilters(final String p) { + assert Thread.holdsLock(this); + assert this.attachmentFormatters != null; + final String list = fromLogManager(p.concat(".attachment.filters")); + if (!isEmpty(list)) { + final String[] names = list.split(","); + Filter[] a = new Filter[names.length]; + for (int i = 0; i < a.length; ++i) { + names[i] = names[i].trim(); + if (!"null".equalsIgnoreCase(names[i])) { + try { + a[i] = LogManagerProperties.newFilter(names[i]); + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (final Exception E) { + reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + } + } + } + + this.attachmentFilters = a; + if (alignAttachmentFilters()) { + reportError("Attachment filters.", + attachmentMismatch("Length mismatch."), ErrorManager.OPEN_FAILURE); + } + } else { + this.attachmentFilters = emptyFilterArray(); + alignAttachmentFilters(); + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initAttachmentFormaters(final String p) { + assert Thread.holdsLock(this); + final String list = fromLogManager(p.concat(".attachment.formatters")); + if (!isEmpty(list)) { + final Formatter[] a; + final String[] names = list.split(","); + if (names.length == 0) { + a = emptyFormatterArray(); + } else { + a = new Formatter[names.length]; + } + + for (int i = 0; i < a.length; ++i) { + names[i] = names[i].trim(); + if (!"null".equalsIgnoreCase(names[i])) { + try { + a[i] = LogManagerProperties.newFormatter(names[i]); + if (a[i] instanceof TailNameFormatter) { + final Exception CNFE = new ClassNotFoundException(a[i].toString()); + reportError("Attachment formatter.", CNFE, ErrorManager.OPEN_FAILURE); + a[i] = createSimpleFormatter(); + } + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (final Exception E) { + reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + a[i] = createSimpleFormatter(); + } + } else { + final Exception NPE = new NullPointerException(atIndexMsg(i)); + reportError("Attachment formatter.", NPE, ErrorManager.OPEN_FAILURE); + a[i] = createSimpleFormatter(); + } + } + + this.attachmentFormatters = a; + } else { + this.attachmentFormatters = emptyFormatterArray(); + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initAttachmentNames(final String p) { + assert Thread.holdsLock(this); + assert this.attachmentFormatters != null; + + final String list = fromLogManager(p.concat(".attachment.names")); + if (!isEmpty(list)) { + final String[] names = list.split(","); + final Formatter[] a = new Formatter[names.length]; + for (int i = 0; i < a.length; ++i) { + names[i] = names[i].trim(); + if (!"null".equalsIgnoreCase(names[i])) { + try { + try { + a[i] = LogManagerProperties.newFormatter(names[i]); + } catch (ClassNotFoundException + | ClassCastException literal) { + a[i] = TailNameFormatter.of(names[i]); + } + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (final Exception E) { + reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + } + } else { + final Exception NPE = new NullPointerException(atIndexMsg(i)); + reportError("Attachment names.", NPE, ErrorManager.OPEN_FAILURE); + } + } + + this.attachmentNames = a; + if (alignAttachmentNames()) { //Any null indexes are repaired. + reportError("Attachment names.", + attachmentMismatch("Length mismatch."), ErrorManager.OPEN_FAILURE); + } + } else { + this.attachmentNames = emptyFormatterArray(); + alignAttachmentNames(); + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initAuthenticator(final String p) { + assert Thread.holdsLock(this); + String name = fromLogManager(p.concat(".authenticator")); + if (name != null && !"null".equalsIgnoreCase(name)) { + if (name.length() != 0) { + try { + this.auth = LogManagerProperties + .newObjectFrom(name, Authenticator.class); + } catch (final SecurityException SE) { + throw SE; + } catch (final ClassNotFoundException + | ClassCastException literalAuth) { + this.auth = DefaultAuthenticator.of(name); + } catch (final Exception E) { + reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + } + } else { //Authenticator is installed to provide the user name. + this.auth = DefaultAuthenticator.of(name); + } + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initLevel(final String p) { + assert Thread.holdsLock(this); + try { + final String val = fromLogManager(p.concat(".level")); + if (val != null) { + logLevel = Level.parse(val); + } else { + logLevel = Level.WARNING; + } + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (final RuntimeException RE) { + reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE); + logLevel = Level.WARNING; + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initFilter(final String p) { + assert Thread.holdsLock(this); + try { + String name = fromLogManager(p.concat(".filter")); + if (hasValue(name)) { + filter = LogManagerProperties.newFilter(name); + } + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (final Exception E) { + reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if argument is null. + * @throws SecurityException if not allowed. + */ + private void initCapacity(final String p) { + assert Thread.holdsLock(this); + final int DEFAULT_CAPACITY = 1000; + try { + final String value = fromLogManager(p.concat(".capacity")); + if (value != null) { + this.setCapacity0(Integer.parseInt(value)); + } else { + this.setCapacity0(DEFAULT_CAPACITY); + } + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (final RuntimeException RE) { + reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE); + } + + if (capacity <= 0) { + capacity = DEFAULT_CAPACITY; + } + + this.data = new LogRecord[1]; + this.matched = new int[this.data.length]; + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initEncoding(final String p) { + assert Thread.holdsLock(this); + try { + String e = fromLogManager(p.concat(".encoding")); + if (e != null) { + setEncoding0(e); + } + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (UnsupportedEncodingException | RuntimeException UEE) { + reportError(UEE.getMessage(), UEE, ErrorManager.OPEN_FAILURE); + } + } + + /** + * Used to get or create the default ErrorManager used before init. + * @return the super error manager or a new ErrorManager. + * @since JavaMail 1.5.3 + */ + private ErrorManager defaultErrorManager() { + ErrorManager em; + try { //Try to share the super error manager. + em = super.getErrorManager(); + } catch (RuntimeException | LinkageError ignore) { + em = null; + } + + //Don't assume that the super call is not null. + if (em == null) { + em = new ErrorManager(); + } + return em; + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initErrorManager(final String p) { + assert Thread.holdsLock(this); + try { + String name = fromLogManager(p.concat(".errorManager")); + if (name != null) { + setErrorManager0(LogManagerProperties.newErrorManager(name)); + } + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (final Exception E) { + reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initFormatter(final String p) { + assert Thread.holdsLock(this); + try { + String name = fromLogManager(p.concat(".formatter")); + if (hasValue(name)) { + final Formatter f + = LogManagerProperties.newFormatter(name); + assert f != null; + if (f instanceof TailNameFormatter == false) { + formatter = f; + } else { + formatter = createSimpleFormatter(); + } + } else { + formatter = createSimpleFormatter(); + } + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (final Exception E) { + reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + formatter = createSimpleFormatter(); + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initComparator(final String p) { + assert Thread.holdsLock(this); + try { + String name = fromLogManager(p.concat(".comparator")); + String reverse = fromLogManager(p.concat(".comparator.reverse")); + if (hasValue(name)) { + comparator = LogManagerProperties.newComparator(name); + if (Boolean.parseBoolean(reverse)) { + assert comparator != null : "null"; + comparator = LogManagerProperties.reverseOrder(comparator); + } + } else { + if (!isEmpty(reverse)) { + throw new IllegalArgumentException( + "No comparator to reverse."); + } + } + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (final Exception E) { + reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initPushLevel(final String p) { + assert Thread.holdsLock(this); + try { + final String val = fromLogManager(p.concat(".pushLevel")); + if (val != null) { + this.pushLevel = Level.parse(val); + } + } catch (final RuntimeException RE) { + reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE); + } + + if (this.pushLevel == null) { + this.pushLevel = Level.OFF; + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initPushFilter(final String p) { + assert Thread.holdsLock(this); + try { + String name = fromLogManager(p.concat(".pushFilter")); + if (hasValue(name)) { + this.pushFilter = LogManagerProperties.newFilter(name); + } + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (final Exception E) { + reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + } + } + + /** + * Parses LogManager string values into objects used by this handler. + * @param p the handler class name used as the prefix. + * @throws NullPointerException if the given argument is null. + * @throws SecurityException if not allowed. + */ + private void initSubject(final String p) { + assert Thread.holdsLock(this); + String name = fromLogManager(p.concat(".subject")); + if (name == null) { //Soft dependency on CollectorFormatter. + name = "com.sun.mail.util.logging.CollectorFormatter"; + } + + if (hasValue(name)) { + try { + this.subjectFormatter = LogManagerProperties.newFormatter(name); + } catch (final SecurityException SE) { + throw SE; //Avoid catch all. + } catch (ClassNotFoundException + | ClassCastException literalSubject) { + this.subjectFormatter = TailNameFormatter.of(name); + } catch (final Exception E) { + this.subjectFormatter = TailNameFormatter.of(name); + reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + } + } else { //User has forced empty or literal null. + this.subjectFormatter = TailNameFormatter.of(name); + } + } + + /** + * Check if any attachment would actually format the given + * LogRecord. This method does not check if the handler + * is level is set to OFF or if the handler is closed. + * @param record a LogRecord + * @return true if the LogRecord would be formatted. + */ + private boolean isAttachmentLoggable(final LogRecord record) { + final Filter[] filters = readOnlyAttachmentFilters(); + for (int i = 0; i < filters.length; ++i) { + final Filter f = filters[i]; + if (f == null || f.isLoggable(record)) { + setMatchedPart(i); + return true; + } + } + return false; + } + + /** + * Check if this Handler would push after storing the + * LogRecord into its internal buffer. + * @param record a LogRecord + * @return true if the LogRecord triggers an email push. + * @throws NullPointerException if tryMutex was not called. + */ + private boolean isPushable(final LogRecord record) { + assert Thread.holdsLock(this); + final int value = getPushLevel().intValue(); + if (value == offValue || record.getLevel().intValue() < value) { + return false; + } + + final Filter push = getPushFilter(); + if (push == null) { + return true; + } + + final int match = getMatchedPart(); + if ((match == -1 && getFilter() == push) + || (match >= 0 && attachmentFilters[match] == push)) { + return true; + } else { + return push.isLoggable(record); + } + } + + /** + * Used to perform push or flush. + * @param priority true for high priority otherwise false for normal. + * @param code the error manager code. + */ + private void push(final boolean priority, final int code) { + if (tryMutex()) { + try { + final Message msg = writeLogRecords(code); + if (msg != null) { + send(msg, priority, code); + } + } catch (final LinkageError JDK8152515) { + reportLinkageError(JDK8152515, code); + } finally { + releaseMutex(); + } + } else { + reportUnPublishedError(null); + } + } + + /** + * Used to send the generated email or write its contents to the + * error manager for this handler. This method does not hold any + * locks so new records can be added to this handler during a send or + * failure. + * @param msg the message or null. + * @param priority true for high priority or false for normal. + * @param code the ErrorManager code. + * @throws NullPointerException if message is null. + */ + private void send(Message msg, boolean priority, int code) { + try { + envelopeFor(msg, priority); + final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER); + try { //JDK-8025251 + Transport.send(msg); //Calls save changes. + } finally { + getAndSetContextClassLoader(ccl); + } + } catch (final RuntimeException re) { + reportError(msg, re, code); + } catch (final Exception e) { + reportError(msg, e, code); + } + } + + /** + * Performs a sort on the records if needed. + * Any exception thrown during a sort is considered a formatting error. + */ + private void sort() { + assert Thread.holdsLock(this); + if (comparator != null) { + try { + if (size != 1) { + Arrays.sort(data, 0, size, comparator); + } else { + if (comparator.compare(data[0], data[0]) != 0) { + throw new IllegalArgumentException( + comparator.getClass().getName()); + } + } + } catch (final RuntimeException RE) { + reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); + } + } + } + + /** + * Formats all records in the buffer and places the output in a Message. + * This method under most conditions will catch, report, and continue when + * exceptions occur. This method holds a lock on this handler. + * @param code the error manager code. + * @return null if there are no records or is currently in a push. + * Otherwise a new message is created with a formatted message and + * attached session. + */ + private Message writeLogRecords(final int code) { + try { + synchronized (this) { + if (size > 0 && !isWriting) { + isWriting = true; + try { + return writeLogRecords0(); + } finally { + isWriting = false; + if (size > 0) { + reset(); + } + } + } + } + } catch (final RuntimeException re) { + reportError(re.getMessage(), re, code); + } catch (final Exception e) { + reportError(e.getMessage(), e, code); + } + return null; + } + + /** + * Formats all records in the buffer and places the output in a Message. + * This method under most conditions will catch, report, and continue when + * exceptions occur. + * + * @return null if there are no records or is currently in a push. Otherwise + * a new message is created with a formatted message and attached session. + * @throws MessagingException if there is a problem. + * @throws IOException if there is a problem. + * @throws RuntimeException if there is an unexpected problem. + * @since JavaMail 1.5.3 + */ + private Message writeLogRecords0() throws Exception { + assert Thread.holdsLock(this); + sort(); + if (session == null) { + initSession(); + } + MimeMessage msg = new MimeMessage(session); + + /** + * Parts are lazily created when an attachment performs a getHead + * call. Therefore, a null part at an index means that the head is + * required. + */ + MimeBodyPart[] parts = new MimeBodyPart[attachmentFormatters.length]; + + /** + * The buffers are lazily created when the part requires a getHead. + */ + StringBuilder[] buffers = new StringBuilder[parts.length]; + StringBuilder buf = null; + final MimePart body; + if (parts.length == 0) { + msg.setDescription(descriptionFrom( + getFormatter(), getFilter(), subjectFormatter)); + body = msg; + } else { + msg.setDescription(descriptionFrom( + comparator, pushLevel, pushFilter)); + body = createBodyPart(); + } + + appendSubject(msg, head(subjectFormatter)); + final Formatter bodyFormat = getFormatter(); + final Filter bodyFilter = getFilter(); + + Locale lastLocale = null; + for (int ix = 0; ix < size; ++ix) { + boolean formatted = false; + final int match = matched[ix]; + final LogRecord r = data[ix]; + data[ix] = null; //Clear while formatting. + + final Locale locale = localeFor(r); + appendSubject(msg, format(subjectFormatter, r)); + Filter lmf = null; //Identity of last matched filter. + if (bodyFilter == null || match == -1 || parts.length == 0 + || (match < -1 && bodyFilter.isLoggable(r))) { + lmf = bodyFilter; + if (buf == null) { + buf = new StringBuilder(); + buf.append(head(bodyFormat)); + } + formatted = true; + buf.append(format(bodyFormat, r)); + if (locale != null && !locale.equals(lastLocale)) { + appendContentLang(body, locale); + } + } + + for (int i = 0; i < parts.length; ++i) { + //A match index less than the attachment index means that + //the filter has not seen this record. + final Filter af = attachmentFilters[i]; + if (af == null || lmf == af || match == i + || (match < i && af.isLoggable(r))) { + if (lmf == null && af != null) { + lmf = af; + } + if (parts[i] == null) { + parts[i] = createBodyPart(i); + buffers[i] = new StringBuilder(); + buffers[i].append(head(attachmentFormatters[i])); + appendFileName(parts[i], head(attachmentNames[i])); + } + formatted = true; + appendFileName(parts[i], format(attachmentNames[i], r)); + buffers[i].append(format(attachmentFormatters[i], r)); + if (locale != null && !locale.equals(lastLocale)) { + appendContentLang(parts[i], locale); + } + } + } + + if (formatted) { + if (body != msg && locale != null + && !locale.equals(lastLocale)) { + appendContentLang(msg, locale); + } + } else { //Belongs to no mime part. + reportFilterError(r); + } + lastLocale = locale; + } + this.size = 0; + + for (int i = parts.length - 1; i >= 0; --i) { + if (parts[i] != null) { + appendFileName(parts[i], tail(attachmentNames[i], "err")); + buffers[i].append(tail(attachmentFormatters[i], "")); + + if (buffers[i].length() > 0) { + String name = parts[i].getFileName(); + if (isEmpty(name)) { //Exceptional case. + name = toString(attachmentFormatters[i]); + parts[i].setFileName(name); + } + setContent(parts[i], buffers[i], getContentType(name)); + } else { + setIncompleteCopy(msg); + parts[i] = null; //Skip this part. + } + buffers[i] = null; + } + } + + if (buf != null) { + buf.append(tail(bodyFormat, "")); + //This body part is always added, even if the buffer is empty, + //so the body is never considered an incomplete-copy. + } else { + buf = new StringBuilder(0); + } + + appendSubject(msg, tail(subjectFormatter, "")); + + String contentType = contentTypeOf(buf); + String altType = contentTypeOf(bodyFormat); + setContent(body, buf, altType == null ? contentType : altType); + if (body != msg) { + final MimeMultipart multipart = new MimeMultipart(); + //assert body instanceof BodyPart : body; + multipart.addBodyPart((BodyPart) body); + + for (int i = 0; i < parts.length; ++i) { + if (parts[i] != null) { + multipart.addBodyPart(parts[i]); + } + } + msg.setContent(multipart); + } + + return msg; + } + + /** + * Checks all of the settings if the caller requests a verify and a verify + * was not performed yet and no verify is in progress. A verify is + * performed on create because this handler may be at the end of a handler + * chain and therefore may not see any log records until LogManager.reset() + * is called and at that time all of the settings have been cleared. + * @param session the current session or null. + * @since JavaMail 1.4.4 + */ + private void verifySettings(final Session session) { + try { + if (session != null) { + final Properties props = session.getProperties(); + final Object check = props.put("verify", ""); + if (check instanceof String) { + String value = (String) check; + //Perform the verify if needed. + if (hasValue(value)) { + verifySettings0(session, value); + } + } else { + if (check != null) { //Pass some invalid string. + verifySettings0(session, check.getClass().toString()); + } + } + } + } catch (final LinkageError JDK8152515) { + reportLinkageError(JDK8152515, ErrorManager.OPEN_FAILURE); + } + } + + /** + * Checks all of the settings using the given setting. + * This triggers the LogManagerProperties to copy all of the mail + * settings without explictly knowing them. Once all of the properties + * are copied this handler can handle LogManager.reset clearing all of the + * properties. It is expected that this method is, at most, only called + * once per session. + * @param session the current session. + * @param verify the type of verify to perform. + * @since JavaMail 1.4.4 + */ + private void verifySettings0(Session session, String verify) { + assert verify != null : (String) null; + if (!"local".equals(verify) && !"remote".equals(verify) + && !"limited".equals(verify) && !"resolve".equals(verify) + && !"login".equals(verify)) { + reportError("Verify must be 'limited', local', " + + "'resolve', 'login', or 'remote'.", + new IllegalArgumentException(verify), + ErrorManager.OPEN_FAILURE); + return; + } + + final MimeMessage abort = new MimeMessage(session); + final String msg; + if (!"limited".equals(verify)) { + msg = "Local address is " + + InternetAddress.getLocalAddress(session) + '.'; + + try { //Verify subclass or declared mime charset. + Charset.forName(getEncodingName()); + } catch (final RuntimeException RE) { + UnsupportedEncodingException UEE = + new UnsupportedEncodingException(RE.toString()); + UEE.initCause(RE); + reportError(msg, UEE, ErrorManager.FORMAT_FAILURE); + } + } else { + msg = "Skipping local address check."; + } + + //Perform all of the copy actions first. + String[] atn; + synchronized (this) { //Create the subject. + appendSubject(abort, head(subjectFormatter)); + appendSubject(abort, tail(subjectFormatter, "")); + atn = new String[attachmentNames.length]; + for (int i = 0; i < atn.length; ++i) { + atn[i] = head(attachmentNames[i]); + if (atn[i].length() == 0) { + atn[i] = tail(attachmentNames[i], ""); + } else { + atn[i] = atn[i].concat(tail(attachmentNames[i], "")); + } + } + } + + setIncompleteCopy(abort); //Original body part is never added. + envelopeFor(abort, true); + saveChangesNoContent(abort, msg); + try { + //Ensure transport provider is installed. + Address[] all = abort.getAllRecipients(); + if (all == null) { //Don't pass null to sendMessage. + all = new InternetAddress[0]; + } + Transport t; + try { + final Address[] any = all.length != 0 ? all : abort.getFrom(); + if (any != null && any.length != 0) { + t = session.getTransport(any[0]); + session.getProperty("mail.transport.protocol"); //Force copy + } else { + MessagingException me = new MessagingException( + "No recipient or from address."); + reportError(msg, me, ErrorManager.OPEN_FAILURE); + throw me; + } + } catch (final MessagingException protocol) { + //Switching the CCL emulates the current send behavior. + Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER); + try { + t = session.getTransport(); + } catch (final MessagingException fail) { + throw attach(protocol, fail); + } finally { + getAndSetContextClassLoader(ccl); + } + } + + String local = null; + if ("remote".equals(verify) || "login".equals(verify)) { + MessagingException closed = null; + t.connect(); + try { + try { + //Capture localhost while connection is open. + local = getLocalHost(t); + + //A message without content will fail at message writeTo + //when sendMessage is called. This allows the handler + //to capture all mail properties set in the LogManager. + if ("remote".equals(verify)) { + t.sendMessage(abort, all); + } + } finally { + try { + t.close(); + } catch (final MessagingException ME) { + closed = ME; + } + } + //Close the transport before reporting errors. + if ("remote".equals(verify)) { + reportUnexpectedSend(abort, verify, null); + } else { + final String protocol = t.getURLName().getProtocol(); + verifyProperties(session, protocol); + } + } catch (final SendFailedException sfe) { + Address[] recip = sfe.getInvalidAddresses(); + if (recip != null && recip.length != 0) { + setErrorContent(abort, verify, sfe); + reportError(abort, sfe, ErrorManager.OPEN_FAILURE); + } + + recip = sfe.getValidSentAddresses(); + if (recip != null && recip.length != 0) { + reportUnexpectedSend(abort, verify, sfe); + } + } catch (final MessagingException ME) { + if (!isMissingContent(abort, ME)) { + setErrorContent(abort, verify, ME); + reportError(abort, ME, ErrorManager.OPEN_FAILURE); + } + } + + if (closed != null) { + setErrorContent(abort, verify, closed); + reportError(abort, closed, ErrorManager.CLOSE_FAILURE); + } + } else { + //Force a property copy, JDK-7092981. + final String protocol = t.getURLName().getProtocol(); + verifyProperties(session, protocol); + String mailHost = session.getProperty("mail." + + protocol + ".host"); + if (isEmpty(mailHost)) { + mailHost = session.getProperty("mail.host"); + } else { + session.getProperty("mail.host"); + } + + local = session.getProperty("mail." + protocol + ".localhost"); + if (isEmpty(local)) { + local = session.getProperty("mail." + + protocol + ".localaddress"); + } else { + session.getProperty("mail." + protocol + ".localaddress"); + } + + if ("resolve".equals(verify)) { + try { //Resolve the remote host name. + String transportHost = t.getURLName().getHost(); + if (!isEmpty(transportHost)) { + verifyHost(transportHost); + if (!transportHost.equalsIgnoreCase(mailHost)) { + verifyHost(mailHost); + } + } else { + verifyHost(mailHost); + } + } catch (final RuntimeException | IOException IOE) { + MessagingException ME = + new MessagingException(msg, IOE); + setErrorContent(abort, verify, ME); + reportError(abort, ME, ErrorManager.OPEN_FAILURE); + } + } + } + + if (!"limited".equals(verify)) { + try { //Verify host name and hit the host name cache. + if (!"remote".equals(verify) && !"login".equals(verify)) { + local = getLocalHost(t); + } + verifyHost(local); + } catch (final RuntimeException | IOException IOE) { + MessagingException ME = new MessagingException(msg, IOE); + setErrorContent(abort, verify, ME); + reportError(abort, ME, ErrorManager.OPEN_FAILURE); + } + + try { //Verify that the DataHandler can be loaded. + Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER); + try { + //Always load the multipart classes. + MimeMultipart multipart = new MimeMultipart(); + MimeBodyPart[] ambp = new MimeBodyPart[atn.length]; + final MimeBodyPart body; + final String bodyContentType; + synchronized (this) { + bodyContentType = contentTypeOf(getFormatter()); + body = createBodyPart(); + for (int i = 0; i < atn.length; ++i) { + ambp[i] = createBodyPart(i); + ambp[i].setFileName(atn[i]); + //Convert names to mime type under lock. + atn[i] = getContentType(atn[i]); + } + } + + body.setDescription(verify); + setContent(body, "", bodyContentType); + multipart.addBodyPart(body); + for (int i = 0; i < ambp.length; ++i) { + ambp[i].setDescription(verify); + setContent(ambp[i], "", atn[i]); + } + + abort.setContent(multipart); + abort.saveChanges(); + abort.writeTo(new ByteArrayOutputStream(MIN_HEADER_SIZE)); + } finally { + getAndSetContextClassLoader(ccl); + } + } catch (final IOException IOE) { + MessagingException ME = new MessagingException(msg, IOE); + setErrorContent(abort, verify, ME); + reportError(abort, ME, ErrorManager.FORMAT_FAILURE); + } + } + + //Verify all recipients. + if (all.length != 0) { + verifyAddresses(all); + } else { + throw new MessagingException("No recipient addresses."); + } + + //Verify from and sender addresses. + Address[] from = abort.getFrom(); + Address sender = abort.getSender(); + if (sender instanceof InternetAddress) { + ((InternetAddress) sender).validate(); + } + + //If from address is declared then check sender. + if (abort.getHeader("From", ",") != null && from.length != 0) { + verifyAddresses(from); + for (int i = 0; i < from.length; ++i) { + if (from[i].equals(sender)) { + MessagingException ME = new MessagingException( + "Sender address '" + sender + + "' equals from address."); + throw new MessagingException(msg, ME); + } + } + } else { + if (sender == null) { + MessagingException ME = new MessagingException( + "No from or sender address."); + throw new MessagingException(msg, ME); + } + } + + //Verify reply-to addresses. + verifyAddresses(abort.getReplyTo()); + } catch (final RuntimeException RE) { + setErrorContent(abort, verify, RE); + reportError(abort, RE, ErrorManager.OPEN_FAILURE); + } catch (final Exception ME) { + setErrorContent(abort, verify, ME); + reportError(abort, ME, ErrorManager.OPEN_FAILURE); + } + } + + /** + * Handles all exceptions thrown when save changes is called on a message + * that doesn't have any content. + * + * @param abort the message requiring save changes. + * @param msg the error description. + * @since JavaMail 1.6.0 + */ + private void saveChangesNoContent(final Message abort, final String msg) { + if (abort != null) { + try { + try { + abort.saveChanges(); + } catch (final NullPointerException xferEncoding) { + //Workaround GNU JavaMail bug in MimeUtility.getEncoding + //when the mime message has no content. + try { + String cte = "Content-Transfer-Encoding"; + if (abort.getHeader(cte) == null) { + abort.setHeader(cte, "base64"); + abort.saveChanges(); + } else { + throw xferEncoding; + } + } catch (RuntimeException | MessagingException e) { + if (e != xferEncoding) { + e.addSuppressed(xferEncoding); + } + throw e; + } + } + } catch (RuntimeException | MessagingException ME) { + reportError(msg, ME, ErrorManager.FORMAT_FAILURE); + } + } + } + + /** + * Cache common session properties into the LogManagerProperties. This is + * a workaround for JDK-7092981. + * + * @param session the session. + * @param protocol the mail protocol. + * @throws NullPointerException if session is null. + * @since JavaMail 1.6.0 + */ + private static void verifyProperties(Session session, String protocol) { + session.getProperty("mail.from"); + session.getProperty("mail." + protocol + ".from"); + session.getProperty("mail.dsn.ret"); + session.getProperty("mail." + protocol + ".dsn.ret"); + session.getProperty("mail.dsn.notify"); + session.getProperty("mail." + protocol + ".dsn.notify"); + session.getProperty("mail." + protocol + ".port"); + session.getProperty("mail.user"); + session.getProperty("mail." + protocol + ".user"); + session.getProperty("mail." + protocol + ".localport"); + } + + /** + * Perform a lookup of the host address or FQDN. + * @param host the host or null. + * @return the address. + * @throws IOException if the host name is not valid. + * @throws SecurityException if security manager is present and doesn't + * allow access to check connect permission. + * @since JavaMail 1.5.0 + */ + private static InetAddress verifyHost(String host) throws IOException { + InetAddress a; + if (isEmpty(host)) { + a = InetAddress.getLocalHost(); + } else { + a = InetAddress.getByName(host); + } + if (a.getCanonicalHostName().length() == 0) { + throw new UnknownHostException(); + } + return a; + } + + /** + * Calls validate for every address given. + * If the addresses given are null, empty or not an InternetAddress then + * the check is skipped. + * @param all any address array, null or empty. + * @throws AddressException if there is a problem. + * @since JavaMail 1.4.5 + */ + private static void verifyAddresses(Address[] all) throws AddressException { + if (all != null) { + for (int i = 0; i < all.length; ++i) { + final Address a = all[i]; + if (a instanceof InternetAddress) { + ((InternetAddress) a).validate(); + } + } + } + } + + /** + * Reports that an empty content message was sent and should not have been. + * @param msg the MimeMessage. + * @param verify the verify enum. + * @param cause the exception that caused the problem or null. + * @since JavaMail 1.4.5 + */ + private void reportUnexpectedSend(MimeMessage msg, String verify, Exception cause) { + final MessagingException write = new MessagingException( + "An empty message was sent.", cause); + setErrorContent(msg, verify, write); + reportError(msg, write, ErrorManager.OPEN_FAILURE); + } + + /** + * Creates and sets the message content from the given Throwable. + * When verify fails, this method fixes the 'abort' message so that any + * created envelope data can be used in the error manager. + * @param msg the message with or without content. + * @param verify the verify enum. + * @param t the throwable or null. + * @since JavaMail 1.4.5 + */ + private void setErrorContent(MimeMessage msg, String verify, Throwable t) { + try { //Add content so toRawString doesn't fail. + final MimeBodyPart body; + final String subjectType; + final String msgDesc; + synchronized (this) { + body = createBodyPart(); + msgDesc = descriptionFrom(comparator, pushLevel, pushFilter); + subjectType = getClassId(subjectFormatter); + } + + body.setDescription("Formatted using " + + (t == null ? Throwable.class.getName() + : t.getClass().getName()) + ", filtered with " + + verify + ", and named by " + + subjectType + '.'); + setContent(body, toMsgString(t), "text/plain"); + final MimeMultipart multipart = new MimeMultipart(); + multipart.addBodyPart(body); + msg.setContent(multipart); + msg.setDescription(msgDesc); + setAcceptLang(msg); + msg.saveChanges(); + } catch (MessagingException | RuntimeException ME) { + reportError("Unable to create body.", ME, ErrorManager.OPEN_FAILURE); + } + } + + /** + * Used to update the cached session object based on changes in + * mail properties or authenticator. + * @return the current session or null if no verify is required. + */ + private Session updateSession() { + assert Thread.holdsLock(this); + final Session settings; + if (mailProps.getProperty("verify") != null) { + settings = initSession(); + assert settings == session : session; + } else { + session = null; //Remove old session. + settings = null; + } + return settings; + } + + /** + * Creates a session using a proxy properties object. + * @return the session that was created and assigned. + */ + private Session initSession() { + assert Thread.holdsLock(this); + final String p = getClass().getName(); + LogManagerProperties proxy = new LogManagerProperties(mailProps, p); + session = Session.getInstance(proxy, auth); + return session; + } + + /** + * Creates all of the envelope information for a message. + * This method is safe to call outside of a lock because the message + * provides the safe snapshot of the mail properties. + * @param msg the Message to write the envelope information. + * @param priority true for high priority. + */ + private void envelopeFor(Message msg, boolean priority) { + setAcceptLang(msg); + setFrom(msg); + if (!setRecipient(msg, "mail.to", Message.RecipientType.TO)) { + setDefaultRecipient(msg, Message.RecipientType.TO); + } + setRecipient(msg, "mail.cc", Message.RecipientType.CC); + setRecipient(msg, "mail.bcc", Message.RecipientType.BCC); + setReplyTo(msg); + setSender(msg); + setMailer(msg); + setAutoSubmitted(msg); + if (priority) { + setPriority(msg); + } + + try { + msg.setSentDate(new java.util.Date()); + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + + /** + * Factory to create the in-line body part. + * @return a body part with default headers set. + * @throws MessagingException if there is a problem. + */ + private MimeBodyPart createBodyPart() throws MessagingException { + assert Thread.holdsLock(this); + final MimeBodyPart part = new MimeBodyPart(); + part.setDisposition(Part.INLINE); + part.setDescription(descriptionFrom(getFormatter(), + getFilter(), subjectFormatter)); + setAcceptLang(part); + return part; + } + + /** + * Factory to create the attachment body part. + * @param index the attachment index. + * @return a body part with default headers set. + * @throws MessagingException if there is a problem. + * @throws IndexOutOfBoundsException if the given index is not an valid + * attachment index. + */ + private MimeBodyPart createBodyPart(int index) throws MessagingException { + assert Thread.holdsLock(this); + final MimeBodyPart part = new MimeBodyPart(); + part.setDisposition(Part.ATTACHMENT); + part.setDescription(descriptionFrom( + attachmentFormatters[index], + attachmentFilters[index], + attachmentNames[index])); + setAcceptLang(part); + return part; + } + + /** + * Gets the description for the MimeMessage itself. + * The push level and filter are included because they play a role in + * formatting of a message when triggered or not triggered. + * @param c the comparator. + * @param l the pushLevel. + * @param f the pushFilter + * @return the description. + * @throws NullPointerException if level is null. + * @since JavaMail 1.4.5 + */ + private String descriptionFrom(Comparator c, Level l, Filter f) { + return "Sorted using "+ (c == null ? "no comparator" + : c.getClass().getName()) + ", pushed when "+ l.getName() + + ", and " + (f == null ? "no push filter" + : f.getClass().getName()) + '.'; + } + + /** + * Creates a description for a body part. + * @param f the content formatter. + * @param filter the content filter. + * @param name the naming formatter. + * @return the description for the body part. + */ + private String descriptionFrom(Formatter f, Filter filter, Formatter name) { + return "Formatted using " + getClassId(f) + + ", filtered with " + (filter == null ? "no filter" + : filter.getClass().getName()) +", and named by " + + getClassId(name) + '.'; + } + + /** + * Gets a class name represents the behavior of the formatter. + * The class name may not be assignable to a Formatter. + * @param f the formatter. + * @return a class name that represents the given formatter. + * @throws NullPointerException if the parameter is null. + * @since JavaMail 1.4.5 + */ + private String getClassId(final Formatter f) { + if (f instanceof TailNameFormatter) { + return String.class.getName(); //Literal string. + } else { + return f.getClass().getName(); + } + } + + /** + * Ensure that a formatter creates a valid string for a part name. + * @param f the formatter. + * @return the to string value or the class name. + */ + private String toString(final Formatter f) { + //Should never be null but, guard against formatter bugs. + final String name = f.toString(); + if (!isEmpty(name)) { + return name; + } else { + return getClassId(f); + } + } + + /** + * Constructs a file name from a formatter. This method is called often + * but, rarely does any work. + * @param part to append to. + * @param chunk non null string to append. + */ + private void appendFileName(final Part part, final String chunk) { + if (chunk != null) { + if (chunk.length() > 0) { + appendFileName0(part, chunk); + } + } else { + reportNullError(ErrorManager.FORMAT_FAILURE); + } + } + + /** + * It is assumed that file names are short and that in most cases + * getTail will be the only method that will produce a result. + * @param part to append to. + * @param chunk non null string to append. + */ + private void appendFileName0(final Part part, String chunk) { + try { + //Remove all control character groups. + chunk = chunk.replaceAll("[\\x00-\\x1F\\x7F]+", ""); + final String old = part.getFileName(); + part.setFileName(old != null ? old.concat(chunk) : chunk); + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + + /** + * Constructs a subject line from a formatter. + * @param msg to append to. + * @param chunk non null string to append. + */ + private void appendSubject(final Message msg, final String chunk) { + if (chunk != null) { + if (chunk.length() > 0) { + appendSubject0(msg, chunk); + } + } else { + reportNullError(ErrorManager.FORMAT_FAILURE); + } + } + + /** + * It is assumed that subject lines are short and that in most cases + * getTail will be the only method that will produce a result. + * @param msg to append to. + * @param chunk non null string to append. + */ + private void appendSubject0(final Message msg, String chunk) { + try { + //Remove all control character groups. + chunk = chunk.replaceAll("[\\x00-\\x1F\\x7F]+", ""); + final String charset = getEncodingName(); + final String old = msg.getSubject(); + assert msg instanceof MimeMessage : msg; + ((MimeMessage) msg).setSubject(old != null ? old.concat(chunk) + : chunk, MimeUtility.mimeCharset(charset)); + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + + /** + * Gets the locale for the given log record from the resource bundle. + * If the resource bundle is using the root locale then the default locale + * is returned. + * @param r the log record. + * @return null if not localized otherwise, the locale of the record. + * @since JavaMail 1.4.5 + */ + private Locale localeFor(final LogRecord r) { + Locale l; + final ResourceBundle rb = r.getResourceBundle(); + if (rb != null) { + l = rb.getLocale(); + if (l == null || isEmpty(l.getLanguage())) { + //The language of the fallback bundle (root) is unknown. + //1. Use default locale. Should only be wrong if the app is + // used with a langauge that was unintended. (unlikely) + //2. Mark it as not localized (force null, info loss). + //3. Use the bundle name (encoded) as an experimental language. + l = Locale.getDefault(); + } + } else { + l = null; + } + return l; + } + + /** + * Appends the content language to the given mime part. + * The language tag is only appended if the given language has not been + * specified. This method is only used when we have LogRecords that are + * localized with an assigned resource bundle. + * @param p the mime part. + * @param l the locale to append. + * @throws NullPointerException if any argument is null. + * @since JavaMail 1.4.5 + */ + private void appendContentLang(final MimePart p, final Locale l) { + try { + String lang = LogManagerProperties.toLanguageTag(l); + if (lang.length() != 0) { + String header = p.getHeader("Content-Language", null); + if (isEmpty(header)) { + p.setHeader("Content-Language", lang); + } else if (!header.equalsIgnoreCase(lang)) { + lang = ",".concat(lang); + int idx = 0; + while ((idx = header.indexOf(lang, idx)) > -1) { + idx += lang.length(); + if (idx == header.length() + || header.charAt(idx) == ',') { + break; + } + } + + if (idx < 0) { + int len = header.lastIndexOf("\r\n\t"); + if (len < 0) { //If not folded. + len = (18 + 2) + header.length(); + } else { + len = (header.length() - len) + 8; + } + + //Perform folding of header if needed. + if ((len + lang.length()) > 76) { + header = header.concat("\r\n\t".concat(lang)); + } else { + header = header.concat(lang); + } + p.setHeader("Content-Language", header); + } + } + } + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + + /** + * Sets the accept language to the default locale of the JVM. + * If the locale is the root locale the header is not added. + * @param p the part to set. + * @since JavaMail 1.4.5 + */ + private void setAcceptLang(final Part p) { + try { + final String lang = LogManagerProperties + .toLanguageTag(Locale.getDefault()); + if (lang.length() != 0) { + p.setHeader("Accept-Language", lang); + } + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + + /** + * Used when a log record was loggable prior to being inserted + * into the buffer but at the time of formatting was no longer loggable. + * Filters were changed after publish but prior to a push or a bug in the + * body filter or one of the attachment filters. + * @param record that was not formatted. + * @since JavaMail 1.4.5 + */ + private void reportFilterError(final LogRecord record) { + assert Thread.holdsLock(this); + final Formatter f = createSimpleFormatter(); + final String msg = "Log record " + record.getSequenceNumber() + + " was filtered from all message parts. " + + head(f) + format(f, record) + tail(f, ""); + final String txt = getFilter() + ", " + + Arrays.asList(readOnlyAttachmentFilters()); + reportError(msg, new IllegalArgumentException(txt), + ErrorManager.FORMAT_FAILURE); + } + + /** + * Reports symmetric contract violations an equals implementation. + * @param o the test object must be non null. + * @param found the possible intern, must be non null. + * @throws NullPointerException if any argument is null. + * @since JavaMail 1.5.0 + */ + private void reportNonSymmetric(final Object o, final Object found) { + reportError("Non symmetric equals implementation." + , new IllegalArgumentException(o.getClass().getName() + + " is not equal to " + found.getClass().getName()) + , ErrorManager.OPEN_FAILURE); + } + + /** + * Reports equals implementations that do not discriminate between objects + * of different types or subclass types. + * @param o the test object must be non null. + * @param found the possible intern, must be non null. + * @throws NullPointerException if any argument is null. + * @since JavaMail 1.5.0 + */ + private void reportNonDiscriminating(final Object o, final Object found) { + reportError("Non discriminating equals implementation." + , new IllegalArgumentException(o.getClass().getName() + + " should not be equal to " + found.getClass().getName()) + , ErrorManager.OPEN_FAILURE); + } + + /** + * Used to outline the bytes to report a null pointer exception. + * See BUD ID 6533165. + * @param code the ErrorManager code. + */ + private void reportNullError(final int code) { + reportError("null", new NullPointerException(), code); + } + + /** + * Creates the head or reports a formatting error. + * @param f the formatter. + * @return the head string or an empty string. + */ + private String head(final Formatter f) { + try { + return f.getHead(this); + } catch (final RuntimeException RE) { + reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); + return ""; + } + } + + /** + * Creates the formatted log record or reports a formatting error. + * @param f the formatter. + * @param r the log record. + * @return the formatted string or an empty string. + */ + private String format(final Formatter f, final LogRecord r) { + try { + return f.format(r); + } catch (final RuntimeException RE) { + reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); + return ""; + } + } + + /** + * Creates the tail or reports a formatting error. + * @param f the formatter. + * @param def the default string to use when there is an error. + * @return the tail string or the given default string. + */ + private String tail(final Formatter f, final String def) { + try { + return f.getTail(this); + } catch (final RuntimeException RE) { + reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); + return def; + } + } + + /** + * Sets the x-mailer header. + * @param msg the target message. + */ + private void setMailer(final Message msg) { + try { + final Class mail = MailHandler.class; + final Class k = getClass(); + String value; + if (k == mail) { + value = mail.getName(); + } else { + try { + value = MimeUtility.encodeText(k.getName()); + } catch (final UnsupportedEncodingException E) { + reportError(E.getMessage(), E, ErrorManager.FORMAT_FAILURE); + value = k.getName().replaceAll("[^\\x00-\\x7F]", "\uu001A"); + } + value = MimeUtility.fold(10, mail.getName() + " using the " + + value + " extension."); + } + msg.setHeader("X-Mailer", value); + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + + /** + * Sets the priority and importance headers. + * @param msg the target message. + */ + private void setPriority(final Message msg) { + try { + msg.setHeader("Importance", "High"); + msg.setHeader("Priority", "urgent"); + msg.setHeader("X-Priority", "2"); //High + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + + /** + * Used to signal that body parts are missing from a message. Also used + * when LogRecords were passed to an attachment formatter but the formatter + * produced no output, which is allowed. Used during a verify because all + * parts are omitted, none of the content formatters are used. This is + * not used when a filter prevents LogRecords from being formatted. + * This header is defined in RFC 2156 and RFC 4021. + * @param msg the message. + * @since JavaMail 1.4.5 + */ + private void setIncompleteCopy(final Message msg) { + try { + msg.setHeader("Incomplete-Copy", ""); + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + + /** + * Signals that this message was generated by automatic process. + * This header is defined in RFC 3834 section 5. + * @param msg the message. + * @since JavaMail 1.4.6 + */ + private void setAutoSubmitted(final Message msg) { + if (allowRestrictedHeaders()) { + try { //RFC 3834 (5.2) + msg.setHeader("auto-submitted", "auto-generated"); + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + } + + /** + * Sets from address header. + * @param msg the target message. + */ + private void setFrom(final Message msg) { + final String from = getSession(msg).getProperty("mail.from"); + if (from != null) { + try { + final Address[] address = InternetAddress.parse(from, false); + if (address.length > 0) { + if (address.length == 1) { + msg.setFrom(address[0]); + } else { //Greater than 1 address. + msg.addFrom(address); + } + } + //Can't place an else statement here because the 'from' is + //not null which causes the local address computation + //to fail. Assume the user wants to omit the from address + //header. + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + setDefaultFrom(msg); + } + } else { + setDefaultFrom(msg); + } + } + + /** + * Sets the from header to the local address. + * @param msg the target message. + */ + private void setDefaultFrom(final Message msg) { + try { + msg.setFrom(); + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + + /** + * Computes the default to-address if none was specified. This can + * fail if the local address can't be computed. + * @param msg the message + * @param type the recipient type. + * @since JavaMail 1.5.0 + */ + private void setDefaultRecipient(final Message msg, + final Message.RecipientType type) { + try { + Address a = InternetAddress.getLocalAddress(getSession(msg)); + if (a != null) { + msg.setRecipient(type, a); + } else { + final MimeMessage m = new MimeMessage(getSession(msg)); + m.setFrom(); //Should throw an exception with a cause. + Address[] from = m.getFrom(); + if (from.length > 0) { + msg.setRecipients(type, from); + } else { + throw new MessagingException("No local address."); + } + } + } catch (MessagingException | RuntimeException ME) { + reportError("Unable to compute a default recipient.", + ME, ErrorManager.FORMAT_FAILURE); + } + } + + /** + * Sets reply-to address header. + * @param msg the target message. + */ + private void setReplyTo(final Message msg) { + final String reply = getSession(msg).getProperty("mail.reply.to"); + if (!isEmpty(reply)) { + try { + final Address[] address = InternetAddress.parse(reply, false); + if (address.length > 0) { + msg.setReplyTo(address); + } + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + } + + /** + * Sets sender address header. + * @param msg the target message. + */ + private void setSender(final Message msg) { + assert msg instanceof MimeMessage : msg; + final String sender = getSession(msg).getProperty("mail.sender"); + if (!isEmpty(sender)) { + try { + final InternetAddress[] address = + InternetAddress.parse(sender, false); + if (address.length > 0) { + ((MimeMessage) msg).setSender(address[0]); + if (address.length > 1) { + reportError("Ignoring other senders.", + tooManyAddresses(address, 1), + ErrorManager.FORMAT_FAILURE); + } + } + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + } + + /** + * A common factory used to create the too many addresses exception. + * @param address the addresses, never null. + * @param offset the starting address to display. + * @return the too many addresses exception. + */ + private AddressException tooManyAddresses(Address[] address, int offset) { + Object l = Arrays.asList(address).subList(offset, address.length); + return new AddressException(l.toString()); + } + + /** + * Sets the recipient for the given message. + * @param msg the message. + * @param key the key to search in the session. + * @param type the recipient type. + * @return true if the key was contained in the session. + */ + private boolean setRecipient(final Message msg, + final String key, final Message.RecipientType type) { + boolean containsKey; + final String value = getSession(msg).getProperty(key); + containsKey = value != null; + if (!isEmpty(value)) { + try { + final Address[] address = InternetAddress.parse(value, false); + if (address.length > 0) { + msg.setRecipients(type, address); + } + } catch (final MessagingException ME) { + reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + } + } + return containsKey; + } + + /** + * Converts an email message to a raw string. This raw string + * is passed to the error manager to allow custom error managers + * to recreate the original MimeMessage object. + * @param msg a Message object. + * @return the raw string or null if msg was null. + * @throws MessagingException if there was a problem with the message. + * @throws IOException if there was a problem. + */ + private String toRawString(final Message msg) throws MessagingException, IOException { + if (msg != null) { + Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER); + try { //JDK-8025251 + int nbytes = Math.max(msg.getSize() + MIN_HEADER_SIZE, MIN_HEADER_SIZE); + ByteArrayOutputStream out = new ByteArrayOutputStream(nbytes); + msg.writeTo(out); //Headers can be UTF-8 or US-ASCII. + return out.toString("UTF-8"); + } finally { + getAndSetContextClassLoader(ccl); + } + } else { //Must match this.reportError behavior, see push method. + return null; //Null is the safe choice. + } + } + + /** + * Converts a throwable to a message string. + * @param t any throwable or null. + * @return the throwable with a stack trace or the literal null. + */ + private String toMsgString(final Throwable t) { + if (t == null) { + return "null"; + } + + final String charset = getEncodingName(); + try { + final ByteArrayOutputStream out = + new ByteArrayOutputStream(MIN_HEADER_SIZE); + + //Create an output stream writer so streams are not double buffered. + try (OutputStreamWriter ows = new OutputStreamWriter(out, charset); + PrintWriter pw = new PrintWriter(ows)) { + pw.println(t.getMessage()); + t.printStackTrace(pw); + pw.flush(); + } //Close OSW before generating string. JDK-6995537 + return out.toString(charset); + } catch (final RuntimeException unexpected) { + return t.toString() + ' ' + unexpected.toString(); + } catch (final Exception badMimeCharset) { + return t.toString() + ' ' + badMimeCharset.toString(); + } + } + + /** + * Replaces the current context class loader with our class loader. + * @param ccl null for boot class loader, a class loader, a class used to + * get the class loader, or a source object to get the class loader. + * @return null for the boot class loader, a class loader, or a marker + * object to signal that no modification was required. + * @since JavaMail 1.5.3 + */ + private Object getAndSetContextClassLoader(final Object ccl) { + if (ccl != GetAndSetContext.NOT_MODIFIED) { + try { + final PrivilegedAction pa; + if (ccl instanceof PrivilegedAction) { + pa = (PrivilegedAction) ccl; + } else { + pa = new GetAndSetContext(ccl); + } + return AccessController.doPrivileged(pa); + } catch (final SecurityException ignore) { + } + } + return GetAndSetContext.NOT_MODIFIED; + } + + /** + * A factory used to create a common attachment mismatch type. + * @param msg the exception message. + * @return a RuntimeException to represent the type of error. + */ + private static RuntimeException attachmentMismatch(final String msg) { + return new IndexOutOfBoundsException(msg); + } + + /** + * Outline the attachment mismatch message. See Bug ID 6533165. + * @param expected the expected array length. + * @param found the array length that was given. + * @return a RuntimeException populated with a message. + */ + private static RuntimeException attachmentMismatch(int expected, int found) { + return attachmentMismatch("Attachments mismatched, expected " + + expected + " but given " + found + '.'); + } + + /** + * Try to attach a suppressed exception to a MessagingException in any order + * that is possible. + * @param required the exception expected to see as a reported failure. + * @param optional the suppressed exception. + * @return either the required or the optional exception. + */ + private static MessagingException attach( + MessagingException required, Exception optional) { + if (optional != null && !required.setNextException(optional)) { + if (optional instanceof MessagingException) { + final MessagingException head = (MessagingException) optional; + if (head.setNextException(required)) { + return head; + } + } + + if (optional != required) { + required.addSuppressed(optional); + } + } + return required; + } + + /** + * Gets the local host from the given service object. + * @param s the service to check. + * @return the local host or null. + * @since JavaMail 1.5.3 + */ + private String getLocalHost(final Service s) { + try { + return LogManagerProperties.getLocalHost(s); + } catch (SecurityException | NoSuchMethodException + | LinkageError ignore) { + } catch (final Exception ex) { + reportError(s.toString(), ex, ErrorManager.OPEN_FAILURE); + } + return null; + } + + /** + * Google App Engine doesn't support Message.getSession. + * @param msg the message. + * @return the session from the given message. + * @throws NullPointerException if the given message is null. + * @since JavaMail 1.5.3 + */ + private Session getSession(final Message msg) { + if (msg == null) { + throw new NullPointerException(); + } + return new MessageContext(msg).getSession(); + } + + /** + * Determines if restricted headers are allowed in the current environment. + * + * @return true if restricted headers are allowed. + * @since JavaMail 1.5.3 + */ + private boolean allowRestrictedHeaders() { + //GAE will prevent delivery of email with forbidden headers. + //Assume the environment is GAE if access to the LogManager is + //forbidden. + return LogManagerProperties.hasLogManager(); + } + + /** + * Outline the creation of the index error message. See JDK-6533165. + * @param i the index. + * @return the error message. + */ + private static String atIndexMsg(final int i) { + return "At index: " + i + '.'; + } + + /** + * Used for storing a password from the LogManager or literal string. + * @since JavaMail 1.4.6 + */ + private static final class DefaultAuthenticator extends Authenticator { + + /** + * Creates an Authenticator for the given password. This method is used + * so class verification of assignments in MailHandler doesn't require + * loading this class which otherwise can occur when using the + * constructor. Default access to avoid generating extra class files. + * + * @param pass the password. + * @return an Authenticator for the password. + * @since JavaMail 1.5.6 + */ + static Authenticator of(final String pass) { + return new DefaultAuthenticator(pass); + } + + /** + * The password to use. + */ + private final String pass; + + /** + * Use the factory method instead of this constructor. + * @param pass the password. + */ + private DefaultAuthenticator(final String pass) { + assert pass != null; + this.pass = pass; + } + + @Override + protected final PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(getDefaultUserName(), pass); + } + } + + /** + * Performs a get and set of the context class loader with privileges + * enabled. + * @since JavaMail 1.4.6 + */ + private static final class GetAndSetContext implements PrivilegedAction { + /** + * A marker object used to signal that the class loader was not + * modified. + */ + public static final Object NOT_MODIFIED = GetAndSetContext.class; + /** + * The source containing the class loader. + */ + private final Object source; + /** + * Create the action. + * @param source null for boot class loader, a class loader, a class + * used to get the class loader, or a source object to get the class + * loader. Default access to avoid generating extra class files. + */ + GetAndSetContext(final Object source) { + this.source = source; + } + + /** + * Gets the class loader from the source and sets the CCL only if + * the source and CCL are not the same. + * @return the replaced context class loader which can be null or + * NOT_MODIFIED to indicate that nothing was modified. + */ + @SuppressWarnings("override") //JDK-6954234 + public final Object run() { + final Thread current = Thread.currentThread(); + final ClassLoader ccl = current.getContextClassLoader(); + final ClassLoader loader; + if (source == null) { + loader = null; //boot class loader + } else if (source instanceof ClassLoader) { + loader = (ClassLoader) source; + } else if (source instanceof Class) { + loader = ((Class) source).getClassLoader(); + } else if (source instanceof Thread) { + loader = ((Thread) source).getContextClassLoader(); + } else { + assert !(source instanceof Class) : source; + loader = source.getClass().getClassLoader(); + } + + if (ccl != loader) { + current.setContextClassLoader(loader); + return ccl; + } else { + return NOT_MODIFIED; + } + } + } + + /** + * Used for naming attachment file names and the main subject line. + */ + private static final class TailNameFormatter extends Formatter { + + /** + * Creates or gets a formatter from the given name. This method is used + * so class verification of assignments in MailHandler doesn't require + * loading this class which otherwise can occur when using the + * constructor. Default access to avoid generating extra class files. + * + * @param name any not null string. + * @return a formatter for that string. + * @since JavaMail 1.5.6 + */ + static Formatter of(final String name) { + return new TailNameFormatter(name); + } + + /** + * The value used as the output. + */ + private final String name; + + /** + * Use the factory method instead of this constructor. + * @param name any not null string. + */ + private TailNameFormatter(final String name) { + assert name != null; + this.name = name; + } + + @Override + public final String format(LogRecord record) { + return ""; + } + + @Override + public final String getTail(Handler h) { + return name; + } + + /** + * Equals method. + * @param o the other object. + * @return true if equal + * @since JavaMail 1.4.4 + */ + @Override + public final boolean equals(Object o) { + if (o instanceof TailNameFormatter) { + return name.equals(((TailNameFormatter) o).name); + } + return false; + } + + /** + * Hash code method. + * @return the hash code. + * @since JavaMail 1.4.4 + */ + @Override + public final int hashCode() { + return getClass().hashCode() + name.hashCode(); + } + + @Override + public final String toString() { + return name; + } + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/logging/SeverityComparator.java b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/SeverityComparator.java new file mode 100644 index 000000000..b571b7b29 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/SeverityComparator.java @@ -0,0 +1,356 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Jason Mehrens. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package com.sun.mail.util.logging; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +/** + * Orders log records by level, thrown, sequence, and time. + * + * This comparator orders LogRecords by how severely each is attributed to + * failures in a program. The primary ordering is determined by the use of the + * logging API throughout a program by specifying a level to each log message. + * The secondary ordering is determined at runtime by the type of errors and + * exceptions generated by the program. The remaining ordering assumes that + * older log records are less severe than newer log records. + * + *

    + * The following LogRecord properties determine severity ordering: + *

      + *
    1. The natural comparison of the LogRecord + * {@linkplain Level#intValue level}. + *
    2. The expected recovery order of {@linkplain LogRecord#getThrown() thrown} + * property of a LogRecord and its cause chain. This ordering is derived from + * the JLS 11.1.1. The Kinds of Exceptions and JLS 11.5 The Exception Hierarchy. + * This is performed by {@linkplain #apply(java.lang.Throwable) finding} the + * throwable that best describes the entire cause chain. Once a specific + * throwable of each chain is identified it is then ranked lowest to highest by + * the following rules: + * + *
        + *
      • All LogRecords with a {@code Throwable} defined as + * "{@link #isNormal(java.lang.Throwable) normal occurrence}". + *
      • All LogRecords that do not have a thrown object. + *
      • All checked exceptions. This is any class that is assignable to the + * {@code java.lang.Throwable} class and is not a + * {@code java.lang.RuntimeException} or a {@code java.lang.Error}. + *
      • All unchecked exceptions. This is all {@code java.lang.RuntimeException} + * objects. + *
      • All errors that indicate a serious problem. This is all + * {@code java.lang.Error} objects. + *
      + *
    3. The natural comparison of the LogRecord + * {@linkplain LogRecord#getSequenceNumber() sequence}. + *
    4. The natural comparison of the LogRecord + * {@linkplain LogRecord#getMillis() millis}. + *
    + * + * @author Jason Mehrens + * @since JavaMail 1.5.2 + */ +public class SeverityComparator implements Comparator, Serializable { + + /** + * The generated serial version UID. + */ + private static final long serialVersionUID = -2620442245251791965L; + + /** + * A single instance that is shared among the logging package. + * The field is declared as java.util.Comparator so + * WebappClassLoader.clearReferencesStaticFinal() method will ignore this + * field. + */ + private static final Comparator INSTANCE + = new SeverityComparator(); + + /** + * A shared instance of a SeverityComparator. This is package private so the + * public API is not polluted with more methods. + * + * @return a shared instance of a SeverityComparator. + */ + static SeverityComparator getInstance() { + return (SeverityComparator) INSTANCE; + } + + /** + * Identifies a single throwable that best describes the given throwable and + * the entire {@linkplain Throwable#getCause() cause} chain. This method can + * be overridden to change the behavior of + * {@link #compare(java.util.logging.LogRecord, java.util.logging.LogRecord)}. + * + * @param chain the throwable or null. + * @return null if null was given, otherwise the throwable that best + * describes the entire chain. + * @see #isNormal(java.lang.Throwable) + */ + public Throwable apply(final Throwable chain) { + //Matches the j.u.f.UnaryOperator interface. + int limit = 0; + Throwable root = chain; + Throwable high = null; + Throwable normal = null; + for (Throwable cause = chain; cause != null; cause = cause.getCause()) { + root = cause; //Find the deepest cause. + + //Find the deepest nomral occurrance. + if (isNormal(cause)) { + normal = cause; + } + + //Find the deepest error that happened before a normal occurance. + if (normal == null && cause instanceof Error) { + high = cause; + } + + //Deal with excessive cause chains and cyclic throwables. + if (++limit == (1 << 16)) { + break; //Give up. + } + } + return high != null ? high : normal != null ? normal : root; + } + + /** + * {@link #apply(java.lang.Throwable) Reduces} each throwable chain argument + * then compare each throwable result. + * + * @param tc1 the first throwable chain or null. + * @param tc2 the second throwable chain or null. + * @return a negative integer, zero, or a positive integer as the first + * argument is less than, equal to, or greater than the second. + * @see #apply(java.lang.Throwable) + * @see #compareThrowable(java.lang.Throwable, java.lang.Throwable) + */ + public final int applyThenCompare(Throwable tc1, Throwable tc2) { + return tc1 == tc2 ? 0 : compareThrowable(apply(tc1), apply(tc2)); + } + + /** + * Compares two throwable objects or null. This method does not + * {@link #apply(java.lang.Throwable) reduce} each argument before + * comparing. This is method can be overridden to change the behavior of + * {@linkplain #compare(LogRecord, LogRecord)}. + * + * @param t1 the first throwable or null. + * @param t2 the second throwable or null. + * @return a negative integer, zero, or a positive integer as the first + * argument is less than, equal to, or greater than the second. + * @see #isNormal(java.lang.Throwable) + */ + public int compareThrowable(final Throwable t1, final Throwable t2) { + if (t1 == t2) { //Reflexive test including null. + return 0; + } else { + //Only one or the other is null at this point. + //Force normal occurrence to be lower than null. + if (t1 == null) { + return isNormal(t2) ? 1 : -1; + } else { + if (t2 == null) { + return isNormal(t1) ? -1 : 1; + } + } + + //From this point on neither are null. + //Follow the shortcut if we can. + if (t1.getClass() == t2.getClass()) { + return 0; + } + + //Ensure normal occurrence flow control is ordered low. + if (isNormal(t1)) { + return isNormal(t2) ? 0 : -1; + } else { + if (isNormal(t2)) { + return 1; + } + } + + //Rank the two unidenticial throwables using the rules from + //JLS 11.1.1. The Kinds of Exceptions and + //JLS 11.5 The Exception Hierarchy. + if (t1 instanceof Error) { + return t2 instanceof Error ? 0 : 1; + } else if (t1 instanceof RuntimeException) { + return t2 instanceof Error ? -1 + : t2 instanceof RuntimeException ? 0 : 1; + } else { + return t2 instanceof Error + || t2 instanceof RuntimeException ? -1 : 0; + } + } + } + + /** + * Compares two log records based on severity. + * + * @param o1 the first log record. + * @param o2 the second log record. + * @return a negative integer, zero, or a positive integer as the first + * argument is less than, equal to, or greater than the second. + * @throws NullPointerException if either argument is null. + */ + @SuppressWarnings("override") //JDK-6954234 + public int compare(final LogRecord o1, final LogRecord o2) { + if (o1 == null || o2 == null) { //Don't allow null. + throw new NullPointerException(toString(o1, o2)); + } + + /** + * LogRecords are mutable so a reflexive relationship test is a safety + * requirement. + */ + if (o1 == o2) { + return 0; + } + + int cmp = compare(o1.getLevel(), o2.getLevel()); + if (cmp == 0) { + cmp = applyThenCompare(o1.getThrown(), o2.getThrown()); + if (cmp == 0) { + cmp = compare(o1.getSequenceNumber(), o2.getSequenceNumber()); + if (cmp == 0) { + cmp = compare(o1.getMillis(), o2.getMillis()); + } + } + } + return cmp; + } + + /** + * Determines if the given object is also a comparator and it imposes the + * same ordering as this comparator. + * + * @param o the reference object with which to compare. + * @return true if this object equal to the argument; false otherwise. + */ + @Override + public boolean equals(final Object o) { + return o == null ? false : o.getClass() == getClass(); + } + + /** + * Returns a hash code value for the object. + * + * @return Returns a hash code value for the object. + */ + @Override + public int hashCode() { + return 31 * getClass().hashCode(); + } + + /** + * Determines if the given throwable instance is "normal occurrence". This + * is any checked or unchecked exception with 'Interrupt' in the class name + * or ancestral class name. Any {@code java.lang.ThreadDeath} object or + * subclasses. + * + * This method can be overridden to change the behavior of the + * {@linkplain #apply(java.lang.Throwable)} method. + * + * @param t a throwable or null. + * @return true the given throwable is a "normal occurrence". + */ + public boolean isNormal(final Throwable t) { + if (t == null) { //This is only needed when called directly. + return false; + } + + /** + * Use the class names to avoid loading more classes. + */ + final Class root = Throwable.class; + final Class error = Error.class; + for (Class c = t.getClass(); c != root; c = c.getSuperclass()) { + if (error.isAssignableFrom(c)) { + if (c.getName().equals("java.lang.ThreadDeath")) { + return true; + } + } else { + //Interrupt, Interrupted or Interruption. + if (c.getName().contains("Interrupt")) { + return true; + } + } + } + return false; + } + + /** + * Compare two level objects. + * + * @param a the first level. + * @param b the second level. + * @return a negative integer, zero, or a positive integer as the first + * argument is less than, equal to, or greater than the second. + */ + private int compare(final Level a, final Level b) { + return a == b ? 0 : compare(a.intValue(), b.intValue()); + } + + /** + * Outline the message create string. + * + * @param o1 argument one. + * @param o2 argument two. + * @return the message string. + */ + private static String toString(final Object o1, final Object o2) { + return o1 + ", " + o2; + } + + /** + * Compare two longs. Can be removed when JDK 1.7 is required. + * + * @param x the first long. + * @param y the second long. + * @return a negative integer, zero, or a positive integer as the first + * argument is less than, equal to, or greater than the second. + */ + private int compare(final long x, final long y) { + return x < y ? -1 : x > y ? 1 : 0; + } +} diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/logging/package.html b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/package.html new file mode 100644 index 000000000..673bdacc5 --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/logging/package.html @@ -0,0 +1,58 @@ + + + + + + + + + + Contains JavaMail™ extensions for + the Java™ platform's core logging + facilities. This package contains classes used to export log messages + as a formatted email message. Classes in this package typically use + LogManager properties to set default values; see the specific + documentation for each concrete class. + + diff --git a/fine-third-default/fine-mail/src/com/sun/mail/util/package.html b/fine-third-default/fine-mail/src/com/sun/mail/util/package.html new file mode 100644 index 000000000..c248b578a --- /dev/null +++ b/fine-third-default/fine-mail/src/com/sun/mail/util/package.html @@ -0,0 +1,84 @@ + + + + + + +com.sun.mail.util package + + + +

    +Utility classes for use with the JavaMail API. +These utility classes are not part of the JavaMail specification. +While this package contains many classes used by the JavaMail implementation +and not intended for direct use by applications, the classes documented +here may be of use to applications. +

    +

    +Classes in this package log debugging information using +{@link java.util.logging} as described in the following table: +

    + + + + + + + + + + + + +
    Logger NameLogging LevelPurpose
    com.sun.mail.util.socketFINERDebugging output related to creating sockets
    + +

    +WARNING: The APIs in this package should be +considered EXPERIMENTAL. They may be changed in the +future in ways that are incompatible with applications using the +current APIs. +

    + + + diff --git a/fine-third-default/fine-mail/src/javax/mail/Address.java b/fine-third-default/fine-mail/src/javax/mail/Address.java new file mode 100644 index 000000000..2c8a6e796 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Address.java @@ -0,0 +1,90 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.io.Serializable; + +/** + * This abstract class models the addresses in a message. + * Subclasses provide specific implementations. Subclasses + * will typically be serializable so that (for example) the + * use of Address objects in search terms can be serialized + * along with the search terms. + * + * @author John Mani + * @author Bill Shannon + */ + +public abstract class Address implements Serializable { + + private static final long serialVersionUID = -5822459626751992278L; + + /** + * Return a type string that identifies this address type. + * + * @return address type + * @see javax.mail.internet.InternetAddress + */ + public abstract String getType(); + + /** + * Return a String representation of this address object. + * + * @return string representation of this address + */ + @Override + public abstract String toString(); + + /** + * The equality operator. Subclasses should provide an + * implementation of this method that supports value equality + * (do the two Address objects represent the same destination?), + * not object reference equality. A subclass must also provide + * a corresponding implementation of the hashCode + * method that preserves the general contract of + * equals and hashCode - objects that + * compare as equal must have the same hashCode. + * + * @param address Address object + */ + @Override + public abstract boolean equals(Object address); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/AuthenticationFailedException.java b/fine-third-default/fine-mail/src/javax/mail/AuthenticationFailedException.java new file mode 100644 index 000000000..cd34e7ee6 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/AuthenticationFailedException.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * This exception is thrown when the connect method on a Store or + * Transport object fails due to an authentication failure (e.g., + * bad user name or password). + * + * @author Bill Shannon + */ + +public class AuthenticationFailedException extends MessagingException { + + private static final long serialVersionUID = 492080754054436511L; + + /** + * Constructs an AuthenticationFailedException. + */ + public AuthenticationFailedException() { + super(); + } + + /** + * Constructs an AuthenticationFailedException with the specified + * detail message. + * + * @param message The detailed error message + */ + public AuthenticationFailedException(String message) { + super(message); + } + + /** + * Constructs an AuthenticationFailedException with the specified + * detail message and embedded exception. The exception is chained + * to this exception. + * + * @param message The detailed error message + * @param e The embedded exception + * @since JavaMail 1.5 + */ + public AuthenticationFailedException(String message, Exception e) { + super(message, e); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Authenticator.java b/fine-third-default/fine-mail/src/javax/mail/Authenticator.java new file mode 100644 index 000000000..68865237c --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Authenticator.java @@ -0,0 +1,162 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.net.InetAddress; + +/** + * The class Authenticator represents an object that knows how to obtain + * authentication for a network connection. Usually, it will do this + * by prompting the user for information. + *

    + * Applications use this class by creating a subclass, and registering + * an instance of that subclass with the session when it is created. + * When authentication is required, the system will invoke a method + * on the subclass (like getPasswordAuthentication). The subclass's + * method can query about the authentication being requested with a + * number of inherited methods (getRequestingXXX()), and form an + * appropriate message for the user. + *

    + * All methods that request authentication have a default implementation + * that fails. + * + * @see java.net.Authenticator + * @see javax.mail.Session#getInstance(java.util.Properties, + * javax.mail.Authenticator) + * @see javax.mail.Session#getDefaultInstance(java.util.Properties, + * javax.mail.Authenticator) + * @see javax.mail.Session#requestPasswordAuthentication + * @see javax.mail.PasswordAuthentication + * + * @author Bill Foote + * @author Bill Shannon + */ + +// There are no abstract methods, but to be useful the user must +// subclass. +public abstract class Authenticator { + + private InetAddress requestingSite; + private int requestingPort; + private String requestingProtocol; + private String requestingPrompt; + private String requestingUserName; + + /** + * Ask the authenticator for a password. + *

    + * + * @param addr The InetAddress of the site requesting authorization, + * or null if not known. + * @param port the port for the requested connection + * @param protocol The protocol that's requesting the connection + * (@see java.net.Authenticator.getProtocol()) + * @param prompt A prompt string for the user + * + * @return The username/password, or null if one can't be gotten. + */ + final synchronized PasswordAuthentication requestPasswordAuthentication( + InetAddress addr, int port, String protocol, + String prompt, String defaultUserName) { + requestingSite = addr; + requestingPort = port; + requestingProtocol = protocol; + requestingPrompt = prompt; + requestingUserName = defaultUserName; + return getPasswordAuthentication(); + } + + /** + * @return the InetAddress of the site requesting authorization, or null + * if it's not available. + */ + protected final InetAddress getRequestingSite() { + return requestingSite; + } + + /** + * @return the port for the requested connection + */ + protected final int getRequestingPort() { + return requestingPort; + } + + /** + * Give the protocol that's requesting the connection. Often this + * will be based on a URLName. + * + * @return the protcol + * + * @see javax.mail.URLName#getProtocol + */ + protected final String getRequestingProtocol() { + return requestingProtocol; + } + + /** + * @return the prompt string given by the requestor + */ + protected final String getRequestingPrompt() { + return requestingPrompt; + } + + /** + * @return the default user name given by the requestor + */ + protected final String getDefaultUserName() { + return requestingUserName; + } + + /** + * Called when password authentication is needed. Subclasses should + * override the default implementation, which returns null.

    + * + * Note that if this method uses a dialog to prompt the user for this + * information, the dialog needs to block until the user supplies the + * information. This method can not simply return after showing the + * dialog. + * @return The PasswordAuthentication collected from the + * user, or null if none is provided. + */ + protected PasswordAuthentication getPasswordAuthentication() { + return null; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/BodyPart.java b/fine-third-default/fine-mail/src/javax/mail/BodyPart.java new file mode 100644 index 000000000..1cb95be7a --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/BodyPart.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * This class models a Part that is contained within a Multipart. + * This is an abstract class. Subclasses provide actual implementations.

    + * + * BodyPart implements the Part interface. Thus, it contains a set of + * attributes and a "content". + * + * @author John Mani + * @author Bill Shannon + */ + +public abstract class BodyPart implements Part { + + /** + * The Multipart object containing this BodyPart, + * if known. + * @since JavaMail 1.1 + */ + protected Multipart parent; + + /** + * Return the containing Multipart object, + * or null if not known. + * + * @return the parent Multipart + */ + public Multipart getParent() { + return parent; + } + + /** + * Set the parent of this BodyPart to be the specified + * Multipart. Normally called by Multipart's + * addBodyPart method. parent may be + * null if the BodyPart is being removed + * from its containing Multipart. + * @since JavaMail 1.1 + */ + void setParent(Multipart parent) { + this.parent = parent; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/EncodingAware.java b/fine-third-default/fine-mail/src/javax/mail/EncodingAware.java new file mode 100644 index 000000000..167da71a0 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/EncodingAware.java @@ -0,0 +1,79 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * A {@link javax.activation.DataSource DataSource} that also implements + * EncodingAware may specify the Content-Transfer-Encoding + * to use for its data. Valid Content-Transfer-Encoding values specified + * by RFC 2045 are "7bit", "8bit", "quoted-printable", "base64", and "binary". + *

    + * For example, a {@link javax.activation.FileDataSource FileDataSource} + * could be created that forces all files to be base64 encoded: + *

    + *  public class Base64FileDataSource extends FileDataSource
    + *					implements EncodingAware {
    + *	public Base64FileDataSource(File file) {
    + *	    super(file);
    + *	}
    + *
    + *	// implements EncodingAware.getEncoding()
    + *	public String getEncoding() {
    + *	    return "base64";
    + *	}
    + *  }
    + * 

    + * + * @since JavaMail 1.5 + * @author Bill Shannon + */ + +public interface EncodingAware { + + /** + * Return the MIME Content-Transfer-Encoding to use for this data, + * or null to indicate that an appropriate value should be chosen + * by the caller. + * + * @return the Content-Transfer-Encoding value, or null + */ + public String getEncoding(); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/EventQueue.java b/fine-third-default/fine-mail/src/javax/mail/EventQueue.java new file mode 100644 index 000000000..938aa1ea0 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/EventQueue.java @@ -0,0 +1,183 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.util.EventListener; +import java.util.Vector; +import java.util.Queue; +import java.util.WeakHashMap; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Executor; +import javax.mail.event.MailEvent; + +/** + * Package private class used by Store & Folder to dispatch events. + * This class implements an event queue, and a dispatcher thread that + * dequeues and dispatches events from the queue. + * + * @author Bill Shannon + */ +class EventQueue implements Runnable { + + private volatile BlockingQueue q; + private Executor executor; + + private static WeakHashMap appq; + + /** + * A special event that causes the queue processing task to terminate. + */ + static class TerminatorEvent extends MailEvent { + private static final long serialVersionUID = -2481895000841664111L; + + TerminatorEvent() { + super(new Object()); + } + + @Override + public void dispatch(Object listener) { + // Kill the event dispatching thread. + Thread.currentThread().interrupt(); + } + } + + /** + * A "struct" to put on the queue. + */ + static class QueueElement { + MailEvent event = null; + Vector vector = null; + + QueueElement(MailEvent event, Vector vector) { + this.event = event; + this.vector = vector; + } + } + + /** + * Construct an EventQueue using the specified Executor. + * If the Executor is null, threads will be created as needed. + */ + EventQueue(Executor ex) { + this.executor = ex; + } + + /** + * Enqueue an event. + */ + synchronized void enqueue(MailEvent event, + Vector vector) { + // if this is the first event, create the queue and start the event task + if (q == null) { + q = new LinkedBlockingQueue<>(); + if (executor != null) { + executor.execute(this); + } else { + Thread qThread = new Thread(this, "JavaMail-EventQueue"); + qThread.setDaemon(true); // not a user thread + qThread.start(); + } + } + q.add(new QueueElement(event, vector)); + } + + /** + * Terminate the task running the queue, but only if there is a queue. + */ + synchronized void terminateQueue() { + if (q != null) { + Vector dummyListeners = new Vector<>(); + dummyListeners.setSize(1); // need atleast one listener + q.add(new QueueElement(new TerminatorEvent(), dummyListeners)); + q = null; + } + } + + /** + * Create (if necessary) an application-scoped event queue. + * Application scoping is based on the thread's context class loader. + */ + static synchronized EventQueue getApplicationEventQueue(Executor ex) { + ClassLoader cl = Session.getContextClassLoader(); + if (appq == null) + appq = new WeakHashMap<>(); + EventQueue q = appq.get(cl); + if (q == null) { + q = new EventQueue(ex); + appq.put(cl, q); + } + return q; + } + + /** + * Pull events off the queue and dispatch them. + */ + @Override + public void run() { + + BlockingQueue bq = q; + if (bq == null) + return; + try { + loop: + for (;;) { + // block until an item is available + QueueElement qe = bq.take(); + MailEvent e = qe.event; + Vector v = qe.vector; + + for (int i = 0; i < v.size(); i++) + try { + e.dispatch(v.elementAt(i)); + } catch (Throwable t) { + if (t instanceof InterruptedException) + break loop; + // ignore anything else thrown by the listener + } + + qe = null; e = null; v = null; + } + } catch (InterruptedException e) { + // just die + } + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/FetchProfile.java b/fine-third-default/fine-mail/src/javax/mail/FetchProfile.java new file mode 100644 index 000000000..c93f78c88 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/FetchProfile.java @@ -0,0 +1,247 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.util.Vector; + +/** + * Clients use a FetchProfile to list the Message attributes that + * it wishes to prefetch from the server for a range of messages.

    + * + * Messages obtained from a Folder are light-weight objects that + * typically start off as empty references to the actual messages. + * Such a Message object is filled in "on-demand" when the appropriate + * get*() methods are invoked on that particular Message. Certain + * server-based message access protocols (Ex: IMAP) allow batch + * fetching of message attributes for a range of messages in a single + * request. Clients that want to use message attributes for a range of + * Messages (Example: to display the top-level headers in a headerlist) + * might want to use the optimization provided by such servers. The + * FetchProfile allows the client to indicate this desire + * to the server.

    + * + * Note that implementations are not obligated to support + * FetchProfiles, since there might be cases where the backend service + * does not allow easy, efficient fetching of such profiles.

    + * + * Sample code that illustrates the use of a FetchProfile is given + * below: + *

    + *
    + *
    + *  Message[] msgs = folder.getMessages();
    + *
    + *  FetchProfile fp = new FetchProfile();
    + *  fp.add(FetchProfile.Item.ENVELOPE);
    + *  fp.add("X-mailer");
    + *  folder.fetch(msgs, fp);
    + *
    + * 

    + * + * @see javax.mail.Folder#fetch + * @author John Mani + * @author Bill Shannon + */ + +public class FetchProfile { + + private Vector specials; // specials + private Vector headers; // vector of header names + + /** + * This inner class is the base class of all items that + * can be requested in a FetchProfile. The items currently + * defined here are ENVELOPE, CONTENT_INFO + * and FLAGS. The UIDFolder interface + * defines the UID Item as well.

    + * + * Note that this class only has a protected constructor, therby + * restricting new Item types to either this class or subclasses. + * This effectively implements a enumeration of allowed Item types. + * + * @see UIDFolder + */ + + public static class Item { + /** + * This is the Envelope item.

    + * + * The Envelope is an aggregration of the common attributes + * of a Message. Implementations should include the following + * attributes: From, To, Cc, Bcc, ReplyTo, Subject and Date. + * More items may be included as well.

    + * + * For implementations of the IMAP4 protocol (RFC 2060), the + * Envelope should include the ENVELOPE data item. More items + * may be included too. + */ + public static final Item ENVELOPE = new Item("ENVELOPE"); + + /** + * This item is for fetching information about the + * content of the message.

    + * + * This includes all the attributes that describe the content + * of the message. Implementations should include the following + * attributes: ContentType, ContentDisposition, + * ContentDescription, Size and LineCount. Other items may be + * included as well. + */ + public static final Item CONTENT_INFO = new Item("CONTENT_INFO"); + + /** + * SIZE is a fetch profile item that can be included in a + * FetchProfile during a fetch request to a Folder. + * This item indicates that the sizes of the messages in the specified + * range should be prefetched.

    + * + * @since JavaMail 1.5 + */ + public static final Item SIZE = new Item("SIZE"); + + /** + * This is the Flags item. + */ + public static final Item FLAGS = new Item("FLAGS"); + + private String name; + + /** + * Constructor for an item. The name is used only for debugging. + * + * @param name the item name + */ + protected Item(String name) { + this.name = name; + } + + /** + * Include the name in the toString return value for debugging. + */ + @Override + public String toString() { + return getClass().getName() + "[" + name + "]"; + } + } + + /** + * Create an empty FetchProfile. + */ + public FetchProfile() { + specials = null; + headers = null; + } + + /** + * Add the given special item as one of the attributes to + * be prefetched. + * + * @param item the special item to be fetched + * @see FetchProfile.Item#ENVELOPE + * @see FetchProfile.Item#CONTENT_INFO + * @see FetchProfile.Item#FLAGS + */ + public void add(Item item) { + if (specials == null) + specials = new Vector<>(); + specials.addElement(item); + } + + /** + * Add the specified header-field to the list of attributes + * to be prefetched. + * + * @param headerName header to be prefetched + */ + public void add(String headerName) { + if (headers == null) + headers = new Vector<>(); + headers.addElement(headerName); + } + + /** + * Returns true if the fetch profile contains the given special item. + * + * @param item the Item to test + * @return true if the fetch profile contains the given special item + */ + public boolean contains(Item item) { + return specials != null && specials.contains(item); + } + + /** + * Returns true if the fetch profile contains the given header name. + * + * @param headerName the header to test + * @return true if the fetch profile contains the given header name + */ + public boolean contains(String headerName) { + return headers != null && headers.contains(headerName); + } + + /** + * Get the items set in this profile. + * + * @return items set in this profile + */ + public Item[] getItems() { + if (specials == null) + return new Item[0]; + + Item[] s = new Item[specials.size()]; + specials.copyInto(s); + return s; + } + + /** + * Get the names of the header-fields set in this profile. + * + * @return headers set in this profile + */ + public String[] getHeaderNames() { + if (headers == null) + return new String[0]; + + String[] s = new String[headers.size()]; + headers.copyInto(s); + return s; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Flags.java b/fine-third-default/fine-mail/src/javax/mail/Flags.java new file mode 100644 index 000000000..bcc092182 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Flags.java @@ -0,0 +1,691 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.io.Serializable; +import java.util.*; + +/** + * The Flags class represents the set of flags on a Message. Flags + * are composed of predefined system flags, and user defined flags.

    + * + * A System flag is represented by the Flags.Flag + * inner class. A User defined flag is represented as a String. + * User flags are case-independent.

    + * + * A set of standard system flags are predefined. Most folder + * implementations are expected to support these flags. Some + * implementations may also support arbitrary user-defined flags. The + * getPermanentFlags method on a Folder returns a Flags + * object that holds all the flags that are supported by that folder + * implementation.

    + * + * A Flags object is serializable so that (for example) the + * use of Flags objects in search terms can be serialized + * along with the search terms.

    + * + * Warning: + * Serialized objects of this class may not be compatible with future + * JavaMail API releases. The current serialization support is + * appropriate for short term storage.

    + * + * The below code sample illustrates how to set, examine, and get the + * flags for a message. + *

    + *
    + * Message m = folder.getMessage(1);
    + * m.setFlag(Flags.Flag.DELETED, true); // set the DELETED flag
    + *
    + * // Check if DELETED flag is set on this message
    + * if (m.isSet(Flags.Flag.DELETED))
    + *	System.out.println("DELETED message");
    + *
    + * // Examine ALL system flags for this message
    + * Flags flags = m.getFlags();
    + * Flags.Flag[] sf = flags.getSystemFlags();
    + * for (int i = 0; i < sf.length; i++) {
    + *	if (sf[i] == Flags.Flag.DELETED)
    + *            System.out.println("DELETED message");
    + *	else if (sf[i] == Flags.Flag.SEEN)
    + *            System.out.println("SEEN message");
    + *      ......
    + *      ......
    + * }
    + * 
    + *

    + * + * @see Folder#getPermanentFlags + * @author John Mani + * @author Bill Shannon + */ + +public class Flags implements Cloneable, Serializable { + + private int system_flags = 0; + // used as a case-independent Set that preserves the original case, + // the key is the lowercase flag name and the value is the original + private Hashtable user_flags = null; + + private final static int ANSWERED_BIT = 0x01; + private final static int DELETED_BIT = 0x02; + private final static int DRAFT_BIT = 0x04; + private final static int FLAGGED_BIT = 0x08; + private final static int RECENT_BIT = 0x10; + private final static int SEEN_BIT = 0x20; + private final static int USER_BIT = 0x80000000; + + private static final long serialVersionUID = 6243590407214169028L; + + /** + * This inner class represents an individual system flag. A set + * of standard system flag objects are predefined here. + */ + public static final class Flag { + /** + * This message has been answered. This flag is set by clients + * to indicate that this message has been answered to. + */ + public static final Flag ANSWERED = new Flag(ANSWERED_BIT); + + /** + * This message is marked deleted. Clients set this flag to + * mark a message as deleted. The expunge operation on a folder + * removes all messages in that folder that are marked for deletion. + */ + public static final Flag DELETED = new Flag(DELETED_BIT); + + /** + * This message is a draft. This flag is set by clients + * to indicate that the message is a draft message. + */ + public static final Flag DRAFT = new Flag(DRAFT_BIT); + + /** + * This message is flagged. No semantic is defined for this flag. + * Clients alter this flag. + */ + public static final Flag FLAGGED = new Flag(FLAGGED_BIT); + + /** + * This message is recent. Folder implementations set this flag + * to indicate that this message is new to this folder, that is, + * it has arrived since the last time this folder was opened.

    + * + * Clients cannot alter this flag. + */ + public static final Flag RECENT = new Flag(RECENT_BIT); + + /** + * This message is seen. This flag is implicitly set by the + * implementation when this Message's content is returned + * to the client in some form. The getInputStream + * and getContent methods on Message cause this + * flag to be set.

    + * + * Clients can alter this flag. + */ + public static final Flag SEEN = new Flag(SEEN_BIT); + + /** + * A special flag that indicates that this folder supports + * user defined flags.

    + * + * The implementation sets this flag. Clients cannot alter + * this flag but can use it to determine if a folder supports + * user defined flags by using + * folder.getPermanentFlags().contains(Flags.Flag.USER). + */ + public static final Flag USER = new Flag(USER_BIT); + + // flags are stored as bits for efficiency + private int bit; + private Flag(int bit) { + this.bit = bit; + } + } + + + /** + * Construct an empty Flags object. + */ + public Flags() { } + + /** + * Construct a Flags object initialized with the given flags. + * + * @param flags the flags for initialization + */ + @SuppressWarnings("unchecked") + public Flags(Flags flags) { + this.system_flags = flags.system_flags; + if (flags.user_flags != null) + this.user_flags = (Hashtable)flags.user_flags.clone(); + } + + /** + * Construct a Flags object initialized with the given system flag. + * + * @param flag the flag for initialization + */ + public Flags(Flag flag) { + this.system_flags |= flag.bit; + } + + /** + * Construct a Flags object initialized with the given user flag. + * + * @param flag the flag for initialization + */ + public Flags(String flag) { + user_flags = new Hashtable<>(1); + user_flags.put(flag.toLowerCase(Locale.ENGLISH), flag); + } + + /** + * Add the specified system flag to this Flags object. + * + * @param flag the flag to add + */ + public void add(Flag flag) { + system_flags |= flag.bit; + } + + /** + * Add the specified user flag to this Flags object. + * + * @param flag the flag to add + */ + public void add(String flag) { + if (user_flags == null) + user_flags = new Hashtable<>(1); + user_flags.put(flag.toLowerCase(Locale.ENGLISH), flag); + } + + /** + * Add all the flags in the given Flags object to this + * Flags object. + * + * @param f Flags object + */ + public void add(Flags f) { + system_flags |= f.system_flags; // add system flags + + if (f.user_flags != null) { // add user-defined flags + if (user_flags == null) + user_flags = new Hashtable<>(1); + + Enumeration e = f.user_flags.keys(); + + while (e.hasMoreElements()) { + String s = e.nextElement(); + user_flags.put(s, f.user_flags.get(s)); + } + } + } + + /** + * Remove the specified system flag from this Flags object. + * + * @param flag the flag to be removed + */ + public void remove(Flag flag) { + system_flags &= ~flag.bit; + } + + /** + * Remove the specified user flag from this Flags object. + * + * @param flag the flag to be removed + */ + public void remove(String flag) { + if (user_flags != null) + user_flags.remove(flag.toLowerCase(Locale.ENGLISH)); + } + + /** + * Remove all flags in the given Flags object from this + * Flags object. + * + * @param f the flag to be removed + */ + public void remove(Flags f) { + system_flags &= ~f.system_flags; // remove system flags + + if (f.user_flags != null) { + if (user_flags == null) + return; + + Enumeration e = f.user_flags.keys(); + while (e.hasMoreElements()) + user_flags.remove(e.nextElement()); + } + } + + /** + * Remove any flags not in the given Flags object. + * Useful for clearing flags not supported by a server. If the + * given Flags object includes the Flags.Flag.USER flag, all user + * flags in this Flags object are retained. + * + * @param f the flags to keep + * @return true if this Flags object changed + * @since JavaMail 1.6 + */ + public boolean retainAll(Flags f) { + boolean changed = false; + int sf = system_flags & f.system_flags; + if (system_flags != sf) { + system_flags = sf; + changed = true; + } + + // if we have user flags, and the USER flag is not set in "f", + // determine which user flags to clear + if (user_flags != null && (f.system_flags & USER_BIT) == 0) { + if (f.user_flags != null) { + Enumeration e = user_flags.keys(); + while (e.hasMoreElements()) { + String key = e.nextElement(); + if (!f.user_flags.containsKey(key)) { + user_flags.remove(key); + changed = true; + } + } + } else { + // if anything in user_flags, throw them away + changed = user_flags.size() > 0; + user_flags = null; + } + } + return changed; + } + + /** + * Check whether the specified system flag is present in this Flags object. + * + * @param flag the flag to test + * @return true of the given flag is present, otherwise false. + */ + public boolean contains(Flag flag) { + return (system_flags & flag.bit) != 0; + } + + /** + * Check whether the specified user flag is present in this Flags object. + * + * @param flag the flag to test + * @return true of the given flag is present, otherwise false. + */ + public boolean contains(String flag) { + if (user_flags == null) + return false; + else + return user_flags.containsKey(flag.toLowerCase(Locale.ENGLISH)); + } + + /** + * Check whether all the flags in the specified Flags object are + * present in this Flags object. + * + * @param f the flags to test + * @return true if all flags in the given Flags object are present, + * otherwise false. + */ + public boolean contains(Flags f) { + // Check system flags + if ((f.system_flags & system_flags) != f.system_flags) + return false; + + // Check user flags + if (f.user_flags != null) { + if (user_flags == null) + return false; + Enumeration e = f.user_flags.keys(); + + while (e.hasMoreElements()) { + if (!user_flags.containsKey(e.nextElement())) + return false; + } + } + + // If we've made it till here, return true + return true; + } + + /** + * Check whether the two Flags objects are equal. + * + * @return true if they're equal + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Flags)) + return false; + + Flags f = (Flags)obj; + + // Check system flags + if (f.system_flags != this.system_flags) + return false; + + // Check user flags + int size = this.user_flags == null ? 0 : this.user_flags.size(); + int fsize = f.user_flags == null ? 0 : f.user_flags.size(); + if (size == 0 && fsize == 0) + return true; + if (f.user_flags != null && this.user_flags != null && fsize == size) + return user_flags.keySet().equals(f.user_flags.keySet()); + + return false; + } + + /** + * Compute a hash code for this Flags object. + * + * @return the hash code + */ + @Override + public int hashCode() { + int hash = system_flags; + if (user_flags != null) { + Enumeration e = user_flags.keys(); + while (e.hasMoreElements()) + hash += e.nextElement().hashCode(); + } + return hash; + } + + /** + * Return all the system flags in this Flags object. Returns + * an array of size zero if no flags are set. + * + * @return array of Flags.Flag objects representing system flags + */ + public Flag[] getSystemFlags() { + Vector v = new Vector<>(); + if ((system_flags & ANSWERED_BIT) != 0) + v.addElement(Flag.ANSWERED); + if ((system_flags & DELETED_BIT) != 0) + v.addElement(Flag.DELETED); + if ((system_flags & DRAFT_BIT) != 0) + v.addElement(Flag.DRAFT); + if ((system_flags & FLAGGED_BIT) != 0) + v.addElement(Flag.FLAGGED); + if ((system_flags & RECENT_BIT) != 0) + v.addElement(Flag.RECENT); + if ((system_flags & SEEN_BIT) != 0) + v.addElement(Flag.SEEN); + if ((system_flags & USER_BIT) != 0) + v.addElement(Flag.USER); + + Flag[] f = new Flag[v.size()]; + v.copyInto(f); + return f; + } + + /** + * Return all the user flags in this Flags object. Returns + * an array of size zero if no flags are set. + * + * @return array of Strings, each String represents a flag. + */ + public String[] getUserFlags() { + Vector v = new Vector<>(); + if (user_flags != null) { + Enumeration e = user_flags.elements(); + + while (e.hasMoreElements()) + v.addElement(e.nextElement()); + } + + String[] f = new String[v.size()]; + v.copyInto(f); + return f; + } + + /** + * Clear all of the system flags. + * + * @since JavaMail 1.6 + */ + public void clearSystemFlags() { + system_flags = 0; + } + + /** + * Clear all of the user flags. + * + * @since JavaMail 1.6 + */ + public void clearUserFlags() { + user_flags = null; + } + + /** + * Returns a clone of this Flags object. + */ + @SuppressWarnings("unchecked") + @Override + public Object clone() { + Flags f = null; + try { + f = (Flags)super.clone(); + } catch (CloneNotSupportedException cex) { + // ignore, can't happen + } + if (this.user_flags != null) + f.user_flags = (Hashtable)this.user_flags.clone(); + return f; + } + + /** + * Return a string representation of this Flags object. + * Note that the exact format of the string is subject to change. + */ + public String toString() { + StringBuilder sb = new StringBuilder(); + + if ((system_flags & ANSWERED_BIT) != 0) + sb.append("\\Answered "); + if ((system_flags & DELETED_BIT) != 0) + sb.append("\\Deleted "); + if ((system_flags & DRAFT_BIT) != 0) + sb.append("\\Draft "); + if ((system_flags & FLAGGED_BIT) != 0) + sb.append("\\Flagged "); + if ((system_flags & RECENT_BIT) != 0) + sb.append("\\Recent "); + if ((system_flags & SEEN_BIT) != 0) + sb.append("\\Seen "); + if ((system_flags & USER_BIT) != 0) + sb.append("\\* "); + + boolean first = true; + if (user_flags != null) { + Enumeration e = user_flags.elements(); + + while (e.hasMoreElements()) { + if (first) + first = false; + else + sb.append(' '); + sb.append(e.nextElement()); + } + } + + if (first && sb.length() > 0) + sb.setLength(sb.length() - 1); // smash trailing space + + return sb.toString(); + } + + /***** + public static void main(String argv[]) throws Exception { + // a new flags object + Flags f1 = new Flags(); + f1.add(Flags.Flag.DELETED); + f1.add(Flags.Flag.SEEN); + f1.add(Flags.Flag.RECENT); + f1.add(Flags.Flag.ANSWERED); + + // check copy constructor with only system flags + Flags fc = new Flags(f1); + if (f1.equals(fc) && fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // check clone with only system flags + fc = (Flags)f1.clone(); + if (f1.equals(fc) && fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // add a user flag and make sure it still works right + f1.add("MyFlag"); + + // shouldn't be equal here + if (!f1.equals(fc) && !fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // check clone + fc = (Flags)f1.clone(); + if (f1.equals(fc) && fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // make sure user flag hash tables are separate + fc.add("AnotherFlag"); + if (!f1.equals(fc) && !fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // check copy constructor + fc = new Flags(f1); + if (f1.equals(fc) && fc.equals(f1)) + System.out.println("success"); + else + System.out.println("fail"); + + // another new flags object + Flags f2 = new Flags(Flags.Flag.ANSWERED); + f2.add("MyFlag"); + + if (f1.contains(Flags.Flag.DELETED)) + System.out.println("success"); + else + System.out.println("fail"); + + if (f1.contains(Flags.Flag.SEEN)) + System.out.println("success"); + else + System.out.println("fail"); + + if (f1.contains(Flags.Flag.RECENT)) + System.out.println("success"); + else + System.out.println("fail"); + + if (f1.contains("MyFlag")) + System.out.println("success"); + else + System.out.println("fail"); + + if (f2.contains(Flags.Flag.ANSWERED)) + System.out.println("success"); + else + System.out.println("fail"); + + + System.out.println("----------------"); + + String[] s = f1.getUserFlags(); + for (int i = 0; i < s.length; i++) + System.out.println(s[i]); + System.out.println("----------------"); + s = f2.getUserFlags(); + for (int i = 0; i < s.length; i++) + System.out.println(s[i]); + + System.out.println("----------------"); + + if (f1.contains(f2)) // this should be true + System.out.println("success"); + else + System.out.println("fail"); + + if (!f2.contains(f1)) // this should be false + System.out.println("success"); + else + System.out.println("fail"); + + Flags f3 = new Flags(); + f3.add(Flags.Flag.DELETED); + f3.add(Flags.Flag.SEEN); + f3.add(Flags.Flag.RECENT); + f3.add(Flags.Flag.ANSWERED); + f3.add("ANOTHERFLAG"); + f3.add("MYFLAG"); + + f1.add("AnotherFlag"); + + if (f1.equals(f3)) + System.out.println("equals success"); + else + System.out.println("fail"); + if (f3.equals(f1)) + System.out.println("equals success"); + else + System.out.println("fail"); + System.out.println("f1 hash code " + f1.hashCode()); + System.out.println("f3 hash code " + f3.hashCode()); + if (f1.hashCode() == f3.hashCode()) + System.out.println("success"); + else + System.out.println("fail"); + } + ****/ +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Folder.java b/fine-third-default/fine-mail/src/javax/mail/Folder.java new file mode 100644 index 000000000..74383c3ca --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Folder.java @@ -0,0 +1,1681 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.lang.*; +import java.util.ArrayList; +import java.util.EventListener; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.Executor; +import javax.mail.search.SearchTerm; +import javax.mail.event.*; + +/** + * Folder is an abstract class that represents a folder for mail + * messages. Subclasses implement protocol specific Folders.

    + * + * Folders can contain Messages, other Folders or both, thus providing + * a tree-like hierarchy rooted at the Store's default folder. (Note + * that some Folder implementations may not allow both Messages and + * other Folders in the same Folder).

    + * + * The interpretation of folder names is implementation dependent. + * The different levels of hierarchy in a folder's full name + * are separated from each other by the hierarchy delimiter + * character.

    + * + * The case-insensitive full folder name (that is, the full name + * relative to the default folder for a Store) INBOX + * is reserved to mean the "primary folder for this user on this + * server". Not all Stores will provide an INBOX folder, and not + * all users will have an INBOX folder at all times. The name + * INBOX is reserved to refer to this folder, + * when it exists, in Stores that provide it.

    + * + * A Folder object obtained from a Store need not actually exist + * in the backend store. The exists method tests whether + * the folder exists or not. The create method + * creates a Folder.

    + * + * A Folder is initially in the closed state. Certain methods are valid + * in this state; the documentation for those methods note this. A + * Folder is opened by calling its 'open' method. All Folder methods, + * except open, delete and + * renameTo, are valid in this state.

    + * + * The only way to get a Folder is by invoking the + * getFolder method on Store, Folder, or Session, or by invoking + * the list or listSubscribed methods + * on Folder. Folder objects returned by the above methods are not + * cached by the Store. Thus, invoking the getFolder method + * with the same folder name multiple times will return distinct Folder + * objects. Likewise for the list and listSubscribed + * methods.

    + * + * The Message objects within the Folder are cached by the Folder. + * Thus, invoking getMessage(msgno) on the same message number + * multiple times will return the same Message object, until an + * expunge is done on this Folder.

    + * + * Message objects from a Folder are only valid while a Folder is open + * and should not be accessed after the Folder is closed, even if the + * Folder is subsequently reopened. Instead, new Message objects must + * be fetched from the Folder after the Folder is reopened.

    + * + * Note that a Message's message number can change within a + * session if the containing Folder is expunged using the expunge + * method. Clients that use message numbers as references to messages + * should be aware of this and should be prepared to deal with this + * situation (probably by flushing out existing message number references + * and reloading them). Because of this complexity, it is better for + * clients to use Message objects as references to messages, rather than + * message numbers. Expunged Message objects still have to be + * pruned, but other Message objects in that folder are not affected by the + * expunge. + * + * @author John Mani + * @author Bill Shannon + */ + +public abstract class Folder implements AutoCloseable { + + /** + * The parent store. + */ + protected Store store; + + /** + * The open mode of this folder. The open mode is + * Folder.READ_ONLY, Folder.READ_WRITE, + * or -1 if not known. + * @since JavaMail 1.1 + */ + protected int mode = -1; + + /* + * The queue of events to be delivered. + */ + private final EventQueue q; + + /** + * Constructor that takes a Store object. + * + * @param store the Store that holds this folder + */ + protected Folder(Store store) { + this.store = store; + + // create or choose the appropriate event queue + Session session = store.getSession(); + String scope = + session.getProperties().getProperty("mail.event.scope", "folder"); + Executor executor = + (Executor)session.getProperties().get("mail.event.executor"); + if (scope.equalsIgnoreCase("application")) + q = EventQueue.getApplicationEventQueue(executor); + else if (scope.equalsIgnoreCase("session")) + q = session.getEventQueue(); + else if (scope.equalsIgnoreCase("store")) + q = store.getEventQueue(); + else // if (scope.equalsIgnoreCase("folder")) + q = new EventQueue(executor); + } + + /** + * Returns the name of this Folder.

    + * + * This method can be invoked on a closed Folder. + * + * @return name of the Folder + */ + public abstract String getName(); + + /** + * Returns the full name of this Folder. If the folder resides under + * the root hierarchy of this Store, the returned name is relative + * to the root. Otherwise an absolute name, starting with the + * hierarchy delimiter, is returned.

    + * + * This method can be invoked on a closed Folder. + * + * @return full name of the Folder + */ + public abstract String getFullName(); + + /** + * Return a URLName representing this folder. The returned URLName + * does not include the password used to access the store. + * + * @return the URLName representing this folder + * @exception MessagingException for failures + * @see URLName + * @since JavaMail 1.1 + */ + public URLName getURLName() throws MessagingException { + URLName storeURL = getStore().getURLName(); + String fullname = getFullName(); + StringBuilder encodedName = new StringBuilder(); + + if (fullname != null) { + /* + // We need to encode each of the folder's names. + char separator = getSeparator(); + StringTokenizer tok = new StringTokenizer( + fullname, new Character(separator).toString(), true); + + while (tok.hasMoreTokens()) { + String s = tok.nextToken(); + if (s.charAt(0) == separator) + encodedName.append(separator); + else + // XXX - should encode, but since there's no decoder... + //encodedName.append(java.net.URLEncoder.encode(s)); + encodedName.append(s); + } + */ + // append the whole thing, until we can encode + encodedName.append(fullname); + } + + /* + * Sure would be convenient if URLName had a + * constructor that took a base URLName. + */ + return new URLName(storeURL.getProtocol(), storeURL.getHost(), + storeURL.getPort(), encodedName.toString(), + storeURL.getUsername(), + null /* no password */); + } + + /** + * Returns the Store that owns this Folder object. + * This method can be invoked on a closed Folder. + * + * @return the Store + */ + public Store getStore() { + return store; + } + + /** + * Returns the parent folder of this folder. + * This method can be invoked on a closed Folder. If this folder + * is the top of a folder hierarchy, this method returns null.

    + * + * Note that since Folder objects are not cached, invoking this method + * returns a new distinct Folder object. + * + * @return Parent folder + * @exception MessagingException for failures + */ + public abstract Folder getParent() throws MessagingException; + + /** + * Tests if this folder physically exists on the Store. + * This method can be invoked on a closed Folder. + * + * @return true if the folder exists, otherwise false + * @see #create + * @exception MessagingException typically if the connection + * to the server is lost. + */ + public abstract boolean exists() throws MessagingException; + + /** + * Returns a list of Folders belonging to this Folder's namespace + * that match the specified pattern. Patterns may contain the wildcard + * characters "%", which matches any character except hierarchy + * delimiters, and "*", which matches any character.

    + * + * As an example, given the folder hierarchy:

    +     *    Personal/
    +     *       Finance/
    +     *          Stocks
    +     *          Bonus
    +     *          StockOptions
    +     *       Jokes
    +     * 
    + * list("*") on "Personal" will return the whole + * hierarchy.
    + * list("%") on "Personal" will return "Finance" and + * "Jokes".
    + * list("Jokes") on "Personal" will return "Jokes".
    + * list("Stock*") on "Finance" will return "Stocks" + * and "StockOptions".

    + * + * Folder objects are not cached by the Store, so invoking this + * method on the same pattern multiple times will return that many + * distinct Folder objects.

    + * + * This method can be invoked on a closed Folder. + * + * @param pattern the match pattern + * @return array of matching Folder objects. An empty + * array is returned if no matching Folders exist. + * @see #listSubscribed + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MessagingException for other failures + */ + public abstract Folder[] list(String pattern) throws MessagingException; + + /** + * Returns a list of subscribed Folders belonging to this Folder's + * namespace that match the specified pattern. If the folder does + * not support subscription, this method should resolve to + * list. + * (The default implementation provided here, does just this). + * The pattern can contain wildcards as for list.

    + * + * Note that, at a given level of the folder hierarchy, a particular + * folder may not be subscribed, but folders underneath that folder + * in the folder hierarchy may be subscribed. In order to allow + * walking the folder hierarchy, such unsubscribed folders may be + * returned, indicating that a folder lower in the hierarchy is + * subscribed. The isSubscribed method on a folder will + * tell whether any particular folder is actually subscribed.

    + * + * Folder objects are not cached by the Store, so invoking this + * method on the same pattern multiple times will return that many + * distinct Folder objects.

    + * + * This method can be invoked on a closed Folder. + * + * @param pattern the match pattern + * @return array of matching subscribed Folder objects. An + * empty array is returned if no matching + * subscribed folders exist. + * @see #list + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MessagingException for other failures + */ + public Folder[] listSubscribed(String pattern) throws MessagingException { + return list(pattern); + } + + /** + * Convenience method that returns the list of folders under this + * Folder. This method just calls the list(String pattern) + * method with "%" as the match pattern. This method can + * be invoked on a closed Folder. + * + * @return array of Folder objects under this Folder. An + * empty array is returned if no subfolders exist. + * @see #list + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MessagingException for other failures + */ + + public Folder[] list() throws MessagingException { + return list("%"); + } + + /** + * Convenience method that returns the list of subscribed folders + * under this Folder. This method just calls the + * listSubscribed(String pattern) method with "%" + * as the match pattern. This method can be invoked on a closed Folder. + * + * @return array of subscribed Folder objects under this + * Folder. An empty array is returned if no subscribed + * subfolders exist. + * @see #listSubscribed + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MessagingException for other failures + */ + public Folder[] listSubscribed() throws MessagingException { + return listSubscribed("%"); + } + + /** + * Return the delimiter character that separates this Folder's pathname + * from the names of immediate subfolders. This method can be invoked + * on a closed Folder. + * + * @exception FolderNotFoundException if the implementation + * requires the folder to exist, but it does not + * @return Hierarchy separator character + */ + public abstract char getSeparator() throws MessagingException; + + /** + * This folder can contain messages + */ + public final static int HOLDS_MESSAGES = 0x01; + + /** + * This folder can contain other folders + */ + public final static int HOLDS_FOLDERS = 0x02; + + /** + * Returns the type of this Folder, that is, whether this folder can hold + * messages or subfolders or both. The returned value is an integer + * bitfield with the appropriate bits set. This method can be invoked + * on a closed folder. + * + * @return integer with appropriate bits set + * @exception FolderNotFoundException if this folder does + * not exist. + * @see #HOLDS_FOLDERS + * @see #HOLDS_MESSAGES + */ + public abstract int getType() throws MessagingException; + + /** + * Create this folder on the Store. When this folder is created, any + * folders in its path that do not exist are also created.

    + * + * If the creation is successful, a CREATED FolderEvent is delivered + * to any FolderListeners registered on this Folder and this Store. + * + * @param type The type of this folder. + * + * @return true if the creation succeeds, else false. + * @exception MessagingException for failures + * @see #HOLDS_FOLDERS + * @see #HOLDS_MESSAGES + * @see javax.mail.event.FolderEvent + */ + public abstract boolean create(int type) throws MessagingException; + + /** + * Returns true if this Folder is subscribed.

    + * + * This method can be invoked on a closed Folder.

    + * + * The default implementation provided here just returns true. + * + * @return true if this Folder is subscribed + */ + public boolean isSubscribed() { + return true; + } + + /** + * Subscribe or unsubscribe this Folder. Not all Stores support + * subscription.

    + * + * This method can be invoked on a closed Folder.

    + * + * The implementation provided here just throws the + * MethodNotSupportedException. + * + * @param subscribe true to subscribe, false to unsubscribe + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MethodNotSupportedException if this store + * does not support subscription + * @exception MessagingException for other failures + */ + public void setSubscribed(boolean subscribe) + throws MessagingException { + throw new MethodNotSupportedException(); + } + + /** + * Returns true if this Folder has new messages since the last time + * this indication was reset. When this indication is set or reset + * depends on the Folder implementation (and in the case of IMAP, + * depends on the server). This method can be used to implement + * a lightweight "check for new mail" operation on a Folder without + * opening it. (For example, a thread that monitors a mailbox and + * flags when it has new mail.) This method should indicate whether + * any messages in the Folder have the RECENT flag set.

    + * + * Note that this is not an incremental check for new mail, i.e., + * it cannot be used to determine whether any new messages have + * arrived since the last time this method was invoked. To + * implement incremental checks, the Folder needs to be opened.

    + * + * This method can be invoked on a closed Folder that can contain + * Messages. + * + * @return true if the Store has new Messages + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MessagingException for other failures + */ + public abstract boolean hasNewMessages() throws MessagingException; + + /** + * Return the Folder object corresponding to the given name. Note that + * this folder does not physically have to exist in the Store. The + * exists() method on a Folder indicates whether it really + * exists on the Store.

    + * + * In some Stores, name can be an absolute path if it starts with the + * hierarchy delimiter. Otherwise, it is interpreted relative to + * this Folder.

    + * + * Folder objects are not cached by the Store, so invoking this + * method on the same name multiple times will return that many + * distinct Folder objects.

    + * + * This method can be invoked on a closed Folder. + * + * @param name name of the Folder + * @return Folder object + * @exception MessagingException for failures + */ + public abstract Folder getFolder(String name) + throws MessagingException; + + /** + * Delete this Folder. This method will succeed only on a closed + * Folder.

    + * + * The recurse flag controls whether the deletion affects + * subfolders or not. If true, all subfolders are deleted, then this + * folder itself is deleted. If false, the behaviour is dependent on + * the folder type and is elaborated below: + * + *

      + *
    • + * The folder can contain only messages: (type == HOLDS_MESSAGES). + *
      + * All messages within the folder are removed. The folder + * itself is then removed. An appropriate FolderEvent is generated by + * the Store and this folder. + * + *
    • + * The folder can contain only subfolders: (type == HOLDS_FOLDERS). + *
      + * If this folder is empty (does not contain any + * subfolders at all), it is removed. An appropriate FolderEvent is + * generated by the Store and this folder.
      + * If this folder contains any subfolders, the delete fails + * and returns false. + * + *
    • + * The folder can contain subfolders as well as messages:
      + * If the folder is empty (no messages or subfolders), it + * is removed. If the folder contains no subfolders, but only messages, + * then all messages are removed. The folder itself is then removed. + * In both the above cases, an appropriate FolderEvent is + * generated by the Store and this folder.

      + * + * If the folder contains subfolders there are 3 possible + * choices an implementation is free to do: + * + *

        + *
      1. The operation fails, irrespective of whether this folder + * contains messages or not. Some implementations might elect to go + * with this simple approach. The delete() method returns false. + * + *
      2. Any messages within the folder are removed. Subfolders + * are not removed. The folder itself is not removed or affected + * in any manner. The delete() method returns true. And the + * exists() method on this folder will return true indicating that + * this folder still exists.
        + * An appropriate FolderEvent is generated by the Store and this folder. + * + *
      3. Any messages within the folder are removed. Subfolders are + * not removed. The folder itself changes its type from + * HOLDS_FOLDERS | HOLDS_MESSAGES to HOLDS_FOLDERS. Thus new + * messages cannot be added to this folder, but new subfolders can + * be created underneath. The delete() method returns true indicating + * success. The exists() method on this folder will return true + * indicating that this folder still exists.
        + * An appropriate FolderEvent is generated by the Store and this folder. + *
      + *
    + * + * @param recurse also delete subfolders? + * @return true if the Folder is deleted successfully + * @exception FolderNotFoundException if this folder does + * not exist + * @exception IllegalStateException if this folder is not in + * the closed state. + * @exception MessagingException for other failures + * @see javax.mail.event.FolderEvent + */ + public abstract boolean delete(boolean recurse) + throws MessagingException; + + /** + * Rename this Folder. This method will succeed only on a closed + * Folder.

    + * + * If the rename is successful, a RENAMED FolderEvent is delivered + * to FolderListeners registered on this folder and its containing + * Store. + * + * @param f a folder representing the new name for this Folder + * @return true if the Folder is renamed successfully + * @exception FolderNotFoundException if this folder does + * not exist + * @exception IllegalStateException if this folder is not in + * the closed state. + * @exception MessagingException for other failures + * @see javax.mail.event.FolderEvent + */ + public abstract boolean renameTo(Folder f) throws MessagingException; + + /** + * The Folder is read only. The state and contents of this + * folder cannot be modified. + */ + public static final int READ_ONLY = 1; + + /** + * The state and contents of this folder can be modified. + */ + public static final int READ_WRITE = 2; + + /** + * Open this Folder. This method is valid only on Folders that + * can contain Messages and that are closed.

    + * + * If this folder is opened successfully, an OPENED ConnectionEvent + * is delivered to any ConnectionListeners registered on this + * Folder.

    + * + * The effect of opening multiple connections to the same folder + * on a specifc Store is implementation dependent. Some implementations + * allow multiple readers, but only one writer. Others allow + * multiple writers as well as readers. + * + * @param mode open the Folder READ_ONLY or READ_WRITE + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception IllegalStateException if this folder is not in + * the closed state. + * @exception MessagingException for other failures + * @see #READ_ONLY + * @see #READ_WRITE + * @see #getType() + * @see javax.mail.event.ConnectionEvent + */ + public abstract void open(int mode) throws MessagingException; + + /** + * Close this Folder. This method is valid only on open Folders.

    + * + * A CLOSED ConnectionEvent is delivered to any ConnectionListeners + * registered on this Folder. Note that the folder is closed even + * if this method terminates abnormally by throwing a + * MessagingException. + * + * @param expunge expunges all deleted messages if this flag is true + * @exception IllegalStateException if this folder is not opened + * @exception MessagingException for other failures + * @see javax.mail.event.ConnectionEvent + */ + public abstract void close(boolean expunge) throws MessagingException; + + /** + * Close this Folder and expunge deleted messages.

    + * + * A CLOSED ConnectionEvent is delivered to any ConnectionListeners + * registered on this Folder. Note that the folder is closed even + * if this method terminates abnormally by throwing a + * MessagingException.

    + * + * This method supports the {@link java.lang.AutoCloseable AutoCloseable} + * interface.

    + * + * This implementation calls close(true). + * + * @exception IllegalStateException if this folder is not opened + * @exception MessagingException for other failures + * @see javax.mail.event.ConnectionEvent + * @since JavaMail 1.6 + */ + @Override + public void close() throws MessagingException { + close(true); + } + + /** + * Indicates whether this Folder is in the 'open' state. + * @return true if this Folder is in the 'open' state. + */ + public abstract boolean isOpen(); + + /** + * Return the open mode of this folder. Returns + * Folder.READ_ONLY, Folder.READ_WRITE, + * or -1 if the open mode is not known (usually only because an older + * Folder provider has not been updated to use this new + * method). + * + * @exception IllegalStateException if this folder is not opened + * @return the open mode of this folder + * @since JavaMail 1.1 + */ + public synchronized int getMode() { + if (!isOpen()) + throw new IllegalStateException("Folder not open"); + return mode; + } + + /** + * Get the permanent flags supported by this Folder. Returns a Flags + * object that contains all the flags supported.

    + * + * The special flag Flags.Flag.USER indicates that this Folder + * supports arbitrary user-defined flags.

    + * + * The supported permanent flags for a folder may not be available + * until the folder is opened. + * + * @return permanent flags, or null if not known + */ + public abstract Flags getPermanentFlags(); + + /** + * Get total number of messages in this Folder.

    + * + * This method can be invoked on a closed folder. However, note + * that for some folder implementations, getting the total message + * count can be an expensive operation involving actually opening + * the folder. In such cases, a provider can choose not to support + * this functionality in the closed state, in which case this method + * must return -1.

    + * + * Clients invoking this method on a closed folder must be aware + * that this is a potentially expensive operation. Clients must + * also be prepared to handle a return value of -1 in this case. + * + * @return total number of messages. -1 may be returned + * by certain implementations if this method is + * invoked on a closed folder. + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MessagingException for other failures + */ + public abstract int getMessageCount() throws MessagingException; + + /** + * Get the number of new messages in this Folder.

    + * + * This method can be invoked on a closed folder. However, note + * that for some folder implementations, getting the new message + * count can be an expensive operation involving actually opening + * the folder. In such cases, a provider can choose not to support + * this functionality in the closed state, in which case this method + * must return -1.

    + * + * Clients invoking this method on a closed folder must be aware + * that this is a potentially expensive operation. Clients must + * also be prepared to handle a return value of -1 in this case.

    + * + * This implementation returns -1 if this folder is closed. Else + * this implementation gets each Message in the folder using + * getMessage(int) and checks whether its + * RECENT flag is set. The total number of messages + * that have this flag set is returned. + * + * @return number of new messages. -1 may be returned + * by certain implementations if this method is + * invoked on a closed folder. + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MessagingException for other failures + */ + public synchronized int getNewMessageCount() + throws MessagingException { + if (!isOpen()) + return -1; + + int newmsgs = 0; + int total = getMessageCount(); + for (int i = 1; i <= total; i++) { + try { + if (getMessage(i).isSet(Flags.Flag.RECENT)) + newmsgs++; + } catch (MessageRemovedException me) { + // This is an expunged message, ignore it. + continue; + } + } + return newmsgs; + } + + /** + * Get the total number of unread messages in this Folder.

    + * + * This method can be invoked on a closed folder. However, note + * that for some folder implementations, getting the unread message + * count can be an expensive operation involving actually opening + * the folder. In such cases, a provider can choose not to support + * this functionality in the closed state, in which case this method + * must return -1.

    + * + * Clients invoking this method on a closed folder must be aware + * that this is a potentially expensive operation. Clients must + * also be prepared to handle a return value of -1 in this case.

    + * + * This implementation returns -1 if this folder is closed. Else + * this implementation gets each Message in the folder using + * getMessage(int) and checks whether its + * SEEN flag is set. The total number of messages + * that do not have this flag set is returned. + * + * @return total number of unread messages. -1 may be returned + * by certain implementations if this method is + * invoked on a closed folder. + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MessagingException for other failures + */ + public synchronized int getUnreadMessageCount() + throws MessagingException { + if (!isOpen()) + return -1; + + int unread = 0; + int total = getMessageCount(); + for (int i = 1; i <= total; i++) { + try { + if (!getMessage(i).isSet(Flags.Flag.SEEN)) + unread++; + } catch (MessageRemovedException me) { + // This is an expunged message, ignore it. + continue; + } + } + return unread; + } + + /** + * Get the number of deleted messages in this Folder.

    + * + * This method can be invoked on a closed folder. However, note + * that for some folder implementations, getting the deleted message + * count can be an expensive operation involving actually opening + * the folder. In such cases, a provider can choose not to support + * this functionality in the closed state, in which case this method + * must return -1.

    + * + * Clients invoking this method on a closed folder must be aware + * that this is a potentially expensive operation. Clients must + * also be prepared to handle a return value of -1 in this case.

    + * + * This implementation returns -1 if this folder is closed. Else + * this implementation gets each Message in the folder using + * getMessage(int) and checks whether its + * DELETED flag is set. The total number of messages + * that have this flag set is returned. + * + * @return number of deleted messages. -1 may be returned + * by certain implementations if this method is + * invoked on a closed folder. + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MessagingException for other failures + * @since JavaMail 1.3 + */ + public synchronized int getDeletedMessageCount() throws MessagingException { + if (!isOpen()) + return -1; + + int deleted = 0; + int total = getMessageCount(); + for (int i = 1; i <= total; i++) { + try { + if (getMessage(i).isSet(Flags.Flag.DELETED)) + deleted++; + } catch (MessageRemovedException me) { + // This is an expunged message, ignore it. + continue; + } + } + return deleted; + } + + /** + * Get the Message object corresponding to the given message + * number. A Message object's message number is the relative + * position of this Message in its Folder. Messages are numbered + * starting at 1 through the total number of message in the folder. + * Note that the message number for a particular Message can change + * during a session if other messages in the Folder are deleted and + * the Folder is expunged.

    + * + * Message objects are light-weight references to the actual message + * that get filled up on demand. Hence Folder implementations are + * expected to provide light-weight Message objects.

    + * + * Unlike Folder objects, repeated calls to getMessage with the + * same message number will return the same Message object, as + * long as no messages in this folder have been expunged.

    + * + * Since message numbers can change within a session if the folder + * is expunged , clients are advised not to use message numbers as + * references to messages. Use Message objects instead. + * + * @param msgnum the message number + * @return the Message object + * @see #getMessageCount + * @see #fetch + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception IllegalStateException if this folder is not opened + * @exception IndexOutOfBoundsException if the message number + * is out of range. + * @exception MessagingException for other failures + */ + public abstract Message getMessage(int msgnum) + throws MessagingException; + + /** + * Get the Message objects for message numbers ranging from start + * through end, both start and end inclusive. Note that message + * numbers start at 1, not 0.

    + * + * Message objects are light-weight references to the actual message + * that get filled up on demand. Hence Folder implementations are + * expected to provide light-weight Message objects.

    + * + * This implementation uses getMessage(index) to obtain the required + * Message objects. Note that the returned array must contain + * (end-start+1) Message objects. + * + * @param start the number of the first message + * @param end the number of the last message + * @return the Message objects + * @see #fetch + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception IllegalStateException if this folder is not opened. + * @exception IndexOutOfBoundsException if the start or end + * message numbers are out of range. + * @exception MessagingException for other failures + */ + public synchronized Message[] getMessages(int start, int end) + throws MessagingException { + Message[] msgs = new Message[end - start +1]; + for (int i = start; i <= end; i++) + msgs[i - start] = getMessage(i); + return msgs; + } + + /** + * Get the Message objects for message numbers specified in + * the array.

    + * + * Message objects are light-weight references to the actual message + * that get filled up on demand. Hence Folder implementations are + * expected to provide light-weight Message objects.

    + * + * This implementation uses getMessage(index) to obtain the required + * Message objects. Note that the returned array must contain + * msgnums.length Message objects + * + * @param msgnums the array of message numbers + * @return the array of Message objects. + * @see #fetch + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception IllegalStateException if this folder is not opened. + * @exception IndexOutOfBoundsException if any message number + * in the given array is out of range. + * @exception MessagingException for other failures + */ + public synchronized Message[] getMessages(int[] msgnums) + throws MessagingException { + int len = msgnums.length; + Message[] msgs = new Message[len]; + for (int i = 0; i < len; i++) + msgs[i] = getMessage(msgnums[i]); + return msgs; + } + + /** + * Get all Message objects from this Folder. Returns an empty array + * if the folder is empty. + * + * Clients can use Message objects (instead of sequence numbers) + * as references to the messages within a folder; this method supplies + * the Message objects to the client. Folder implementations are + * expected to provide light-weight Message objects, which get + * filled on demand.

    + * + * This implementation invokes getMessageCount() to get + * the current message count and then uses getMessage() + * to get Message objects from 1 till the message count. + * + * @return array of Message objects, empty array if folder + * is empty. + * @see #fetch + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception IllegalStateException if this folder is not opened. + * @exception MessagingException for other failures + */ + public synchronized Message[] getMessages() throws MessagingException { + if (!isOpen()) // otherwise getMessageCount might return -1 + throw new IllegalStateException("Folder not open"); + int total = getMessageCount(); + Message[] msgs = new Message[total]; + for (int i = 1; i <= total; i++) + msgs[i-1] = getMessage(i); + return msgs; + } + + /** + * Append given Messages to this folder. This method can be + * invoked on a closed Folder. An appropriate MessageCountEvent + * is delivered to any MessageCountListener registered on this + * folder when the messages arrive in the folder.

    + * + * Folder implementations must not abort this operation if a + * Message in the given message array turns out to be an + * expunged Message. + * + * @param msgs array of Messages to be appended + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception MessagingException if the append failed. + */ + public abstract void appendMessages(Message[] msgs) + throws MessagingException; + + /** + * Prefetch the items specified in the FetchProfile for the + * given Messages.

    + * + * Clients use this method to indicate that the specified items are + * needed en-masse for the given message range. Implementations are + * expected to retrieve these items for the given message range in + * a efficient manner. Note that this method is just a hint to the + * implementation to prefetch the desired items.

    + * + * An example is a client filling its header-view window with + * the Subject, From and X-mailer headers for all messages in the + * folder. + *

    +     *
    +     *  Message[] msgs = folder.getMessages();
    +     *
    +     *  FetchProfile fp = new FetchProfile();
    +     *  fp.add(FetchProfile.Item.ENVELOPE);
    +     *  fp.add("X-mailer");
    +     *  folder.fetch(msgs, fp);
    +     *  
    +     *  for (int i = 0; i < folder.getMessageCount(); i++) {
    +     *      display(msg[i].getFrom());
    +     *      display(msg[i].getSubject());
    +     *      display(msg[i].getHeader("X-mailer"));
    +     *  }
    +     *
    +     * 

    + * + * The implementation provided here just returns without + * doing anything useful. Providers wanting to provide a real + * implementation for this method should override this method. + * + * @param msgs fetch items for these messages + * @param fp the FetchProfile + * @exception IllegalStateException if this folder is not opened + * @exception MessagingException for other failures + */ + public void fetch(Message[] msgs, FetchProfile fp) + throws MessagingException { + return; + } + + /** + * Set the specified flags on the messages specified in the array. + * This will result in appropriate MessageChangedEvents being + * delivered to any MessageChangedListener registered on this + * Message's containing folder.

    + * + * Note that the specified Message objects must + * belong to this folder. Certain Folder implementations can + * optimize the operation of setting Flags for a group of messages, + * so clients might want to use this method, rather than invoking + * Message.setFlags for each Message.

    + * + * This implementation degenerates to invoking setFlags() + * on each Message object. Specific Folder implementations that can + * optimize this case should do so. + * Also, an implementation must not abort the operation if a Message + * in the array turns out to be an expunged Message. + * + * @param msgs the array of message objects + * @param flag Flags object containing the flags to be set + * @param value set the flags to this boolean value + * @exception IllegalStateException if this folder is not opened + * or if it has been opened READ_ONLY. + * @exception MessagingException for other failures + * @see Message#setFlags + * @see javax.mail.event.MessageChangedEvent + */ + public synchronized void setFlags(Message[] msgs, + Flags flag, boolean value) throws MessagingException { + for (int i = 0; i < msgs.length; i++) { + try { + msgs[i].setFlags(flag, value); + } catch (MessageRemovedException me) { + // This message is expunged, skip + } + } + } + + /** + * Set the specified flags on the messages numbered from start + * through end, both start and end inclusive. Note that message + * numbers start at 1, not 0. + * This will result in appropriate MessageChangedEvents being + * delivered to any MessageChangedListener registered on this + * Message's containing folder.

    + * + * Certain Folder implementations can + * optimize the operation of setting Flags for a group of messages, + * so clients might want to use this method, rather than invoking + * Message.setFlags for each Message.

    + * + * The default implementation uses getMessage(int) to + * get each Message object and then invokes + * setFlags on that object to set the flags. + * Specific Folder implementations that can optimize this case should do so. + * Also, an implementation must not abort the operation if a message + * number refers to an expunged message. + * + * @param start the number of the first message + * @param end the number of the last message + * @param flag Flags object containing the flags to be set + * @param value set the flags to this boolean value + * @exception IllegalStateException if this folder is not opened + * or if it has been opened READ_ONLY. + * @exception IndexOutOfBoundsException if the start or end + * message numbers are out of range. + * @exception MessagingException for other failures + * @see Message#setFlags + * @see javax.mail.event.MessageChangedEvent + */ + public synchronized void setFlags(int start, int end, + Flags flag, boolean value) throws MessagingException { + for (int i = start; i <= end; i++) { + try { + Message msg = getMessage(i); + msg.setFlags(flag, value); + } catch (MessageRemovedException me) { + // This message is expunged, skip + } + } + } + + /** + * Set the specified flags on the messages whose message numbers + * are in the array. + * This will result in appropriate MessageChangedEvents being + * delivered to any MessageChangedListener registered on this + * Message's containing folder.

    + * + * Certain Folder implementations can + * optimize the operation of setting Flags for a group of messages, + * so clients might want to use this method, rather than invoking + * Message.setFlags for each Message.

    + * + * The default implementation uses getMessage(int) to + * get each Message object and then invokes + * setFlags on that object to set the flags. + * Specific Folder implementations that can optimize this case should do so. + * Also, an implementation must not abort the operation if a message + * number refers to an expunged message. + * + * @param msgnums the array of message numbers + * @param flag Flags object containing the flags to be set + * @param value set the flags to this boolean value + * @exception IllegalStateException if this folder is not opened + * or if it has been opened READ_ONLY. + * @exception IndexOutOfBoundsException if any message number + * in the given array is out of range. + * @exception MessagingException for other failures + * @see Message#setFlags + * @see javax.mail.event.MessageChangedEvent + */ + public synchronized void setFlags(int[] msgnums, + Flags flag, boolean value) throws MessagingException { + for (int i = 0; i < msgnums.length; i++) { + try { + Message msg = getMessage(msgnums[i]); + msg.setFlags(flag, value); + } catch (MessageRemovedException me) { + // This message is expunged, skip + } + } + } + + /** + * Copy the specified Messages from this Folder into another + * Folder. This operation appends these Messages to the + * destination Folder. The destination Folder does not have to + * be opened. An appropriate MessageCountEvent + * is delivered to any MessageCountListener registered on the + * destination folder when the messages arrive in the folder.

    + * + * Note that the specified Message objects must + * belong to this folder. Folder implementations might be able + * to optimize this method by doing server-side copies.

    + * + * This implementation just invokes appendMessages() + * on the destination folder to append the given Messages. Specific + * folder implementations that support server-side copies should + * do so, if the destination folder's Store is the same as this + * folder's Store. + * Also, an implementation must not abort the operation if a + * Message in the array turns out to be an expunged Message. + * + * @param msgs the array of message objects + * @param folder the folder to copy the messages to + * @exception FolderNotFoundException if the destination + * folder does not exist. + * @exception IllegalStateException if this folder is not opened. + * @exception MessagingException for other failures + * @see #appendMessages + */ + public void copyMessages(Message[] msgs, Folder folder) + throws MessagingException { + if (!folder.exists()) + throw new FolderNotFoundException( + folder.getFullName() + " does not exist", + folder); + + folder.appendMessages(msgs); + } + + /** + * Expunge (permanently remove) messages marked DELETED. Returns an + * array containing the expunged message objects. The + * getMessageNumber method + * on each of these message objects returns that Message's original + * (that is, prior to the expunge) sequence number. A MessageCountEvent + * containing the expunged messages is delivered to any + * MessageCountListeners registered on the folder.

    + * + * Expunge causes the renumbering of Message objects subsequent to + * the expunged messages. Clients that use message numbers as + * references to messages should be aware of this and should be + * prepared to deal with the situation (probably by flushing out + * existing message number caches and reloading them). Because of + * this complexity, it is better for clients to use Message objects + * as references to messages, rather than message numbers. Any + * expunged Messages objects still have to be pruned, but other + * Messages in that folder are not affected by the expunge.

    + * + * After a message is expunged, only the isExpunged and + * getMessageNumber methods are still valid on the + * corresponding Message object; other methods may throw + * MessageRemovedException + * + * @return array of expunged Message objects + * @exception FolderNotFoundException if this folder does not + * exist + * @exception IllegalStateException if this folder is not opened. + * @exception MessagingException for other failures + * @see Message#isExpunged + * @see javax.mail.event.MessageCountEvent + */ + public abstract Message[] expunge() throws MessagingException; + + /** + * Search this Folder for messages matching the specified + * search criterion. Returns an array containing the matching + * messages . Returns an empty array if no matches were found.

    + * + * This implementation invokes + * search(term, getMessages()), to apply the search + * over all the messages in this folder. Providers that can implement + * server-side searching might want to override this method to provide + * a more efficient implementation. + * + * @param term the search criterion + * @return array of matching messages + * @exception javax.mail.search.SearchException if the search + * term is too complex for the implementation to handle. + * @exception FolderNotFoundException if this folder does + * not exist. + * @exception IllegalStateException if this folder is not opened. + * @exception MessagingException for other failures + * @see javax.mail.search.SearchTerm + */ + public Message[] search(SearchTerm term) throws MessagingException { + return search(term, getMessages()); + } + + /** + * Search the given array of messages for those that match the + * specified search criterion. Returns an array containing the + * matching messages. Returns an empty array if no matches were + * found.

    + * + * Note that the specified Message objects must + * belong to this folder.

    + * + * This implementation iterates through the given array of messages, + * and applies the search criterion on each message by calling + * its match() method with the given term. The + * messages that succeed in the match are returned. Providers + * that can implement server-side searching might want to override + * this method to provide a more efficient implementation. If the + * search term is too complex or contains user-defined terms that + * cannot be executed on the server, providers may elect to either + * throw a SearchException or degenerate to client-side searching by + * calling super.search() to invoke this implementation. + * + * @param term the search criterion + * @param msgs the messages to be searched + * @return array of matching messages + * @exception javax.mail.search.SearchException if the search + * term is too complex for the implementation to handle. + * @exception IllegalStateException if this folder is not opened + * @exception MessagingException for other failures + * @see javax.mail.search.SearchTerm + */ + public Message[] search(SearchTerm term, Message[] msgs) + throws MessagingException { + List matchedMsgs = new ArrayList<>(); + + // Run thru the given messages + for (Message msg : msgs) { + try { + if (msg.match(term)) // matched + matchedMsgs.add(msg); // add it + } catch(MessageRemovedException mrex) { } + } + + return matchedMsgs.toArray(new Message[matchedMsgs.size()]); + } + + /* + * The set of listeners are stored in Vectors appropriate to their + * type. We mark all listener Vectors as "volatile" because, while + * we initialize them inside this folder's synchronization lock, + * they are accessed (checked for null) in the "notify" methods, + * which can't be synchronized due to lock ordering constraints. + * Since the listener fields (the handles on the Vector objects) + * are only ever set, and are never cleared, we believe this is + * safe. The code that dispatches the notifications will either + * see the null and assume there are no listeners or will see the + * Vector and will process the listeners. There's an inherent race + * between adding a listener and notifying the listeners; the lack + * of synchronization during notification does not make the race + * condition significantly worse. If one thread is setting a + * listener at the "same" time an event is being dispatched, the + * dispatch code might not see the listener right away. The + * dispatch code doesn't have to worry about the Vector handle + * being set to null, and thus using an out-of-date set of + * listeners, because we never set the field to null. + */ + + // Vector of connection listeners. + private volatile Vector connectionListeners = null; + + /** + * Add a listener for Connection events on this Folder.

    + * + * The implementation provided here adds this listener + * to an internal list of ConnectionListeners. + * + * @param l the Listener for Connection events + * @see javax.mail.event.ConnectionEvent + */ + public synchronized void + addConnectionListener(ConnectionListener l) { + if (connectionListeners == null) + connectionListeners = new Vector<>(); + connectionListeners.addElement(l); + } + + /** + * Remove a Connection event listener.

    + * + * The implementation provided here removes this listener + * from the internal list of ConnectionListeners. + * + * @param l the listener + * @see #addConnectionListener + */ + public synchronized void + removeConnectionListener(ConnectionListener l) { + if (connectionListeners != null) + connectionListeners.removeElement(l); + } + + /** + * Notify all ConnectionListeners. Folder implementations are + * expected to use this method to broadcast connection events.

    + * + * The provided implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the registered + * ConnectionListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param type the ConnectionEvent type + * @see javax.mail.event.ConnectionEvent + */ + protected void notifyConnectionListeners(int type) { + if (connectionListeners != null) { + ConnectionEvent e = new ConnectionEvent(this, type); + queueEvent(e, connectionListeners); + } + + /* Fix for broken JDK1.1.x Garbage collector : + * The 'conservative' GC in JDK1.1.x occasionally fails to + * garbage-collect Threads which are in the wait state. + * This would result in thread (and consequently memory) leaks. + * + * We attempt to fix this by sending a 'terminator' event + * to the queue, after we've sent the CLOSED event. The + * terminator event causes the event-dispatching thread to + * self destruct. + */ + if (type == ConnectionEvent.CLOSED) + q.terminateQueue(); + } + + // Vector of folder listeners + private volatile Vector folderListeners = null; + + /** + * Add a listener for Folder events on this Folder.

    + * + * The implementation provided here adds this listener + * to an internal list of FolderListeners. + * + * @param l the Listener for Folder events + * @see javax.mail.event.FolderEvent + */ + public synchronized void addFolderListener(FolderListener l) { + if (folderListeners == null) + folderListeners = new Vector<>(); + folderListeners.addElement(l); + } + + /** + * Remove a Folder event listener.

    + * + * The implementation provided here removes this listener + * from the internal list of FolderListeners. + * + * @param l the listener + * @see #addFolderListener + */ + public synchronized void removeFolderListener(FolderListener l) { + if (folderListeners != null) + folderListeners.removeElement(l); + } + + /** + * Notify all FolderListeners registered on this Folder and + * this folder's Store. Folder implementations are expected + * to use this method to broadcast Folder events.

    + * + * The implementation provided here queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the + * FolderListeners registered on this folder. The implementation + * also invokes notifyFolderListeners on this folder's + * Store to notify any FolderListeners registered on the store. + * + * @param type type of FolderEvent + * @see #notifyFolderRenamedListeners + */ + protected void notifyFolderListeners(int type) { + if (folderListeners != null) { + FolderEvent e = new FolderEvent(this, this, type); + queueEvent(e, folderListeners); + } + store.notifyFolderListeners(type, this); + } + + /** + * Notify all FolderListeners registered on this Folder and + * this folder's Store about the renaming of this folder. + * Folder implementations are expected to use this method to + * broadcast Folder events indicating the renaming of folders.

    + * + * The implementation provided here queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the + * FolderListeners registered on this folder. The implementation + * also invokes notifyFolderRenamedListeners on this + * folder's Store to notify any FolderListeners registered on the store. + * + * @param folder Folder representing the new name. + * @see #notifyFolderListeners + * @since JavaMail 1.1 + */ + protected void notifyFolderRenamedListeners(Folder folder) { + if (folderListeners != null) { + FolderEvent e = new FolderEvent(this, this, folder, + FolderEvent.RENAMED); + queueEvent(e, folderListeners); + } + store.notifyFolderRenamedListeners(this, folder); + } + + // Vector of MessageCount listeners + private volatile Vector messageCountListeners = null; + + /** + * Add a listener for MessageCount events on this Folder.

    + * + * The implementation provided here adds this listener + * to an internal list of MessageCountListeners. + * + * @param l the Listener for MessageCount events + * @see javax.mail.event.MessageCountEvent + */ + public synchronized void addMessageCountListener(MessageCountListener l) { + if (messageCountListeners == null) + messageCountListeners = new Vector<>(); + messageCountListeners.addElement(l); + } + + /** + * Remove a MessageCount listener.

    + * + * The implementation provided here removes this listener + * from the internal list of MessageCountListeners. + * + * @param l the listener + * @see #addMessageCountListener + */ + public synchronized void + removeMessageCountListener(MessageCountListener l) { + if (messageCountListeners != null) + messageCountListeners.removeElement(l); + } + + /** + * Notify all MessageCountListeners about the addition of messages + * into this folder. Folder implementations are expected to use this + * method to broadcast MessageCount events for indicating arrival of + * new messages.

    + * + * The provided implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the registered + * MessageCountListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param msgs the messages that were added + */ + protected void notifyMessageAddedListeners(Message[] msgs) { + if (messageCountListeners == null) + return; + + MessageCountEvent e = new MessageCountEvent( + this, + MessageCountEvent.ADDED, + false, + msgs); + + queueEvent(e, messageCountListeners); + } + + /** + * Notify all MessageCountListeners about the removal of messages + * from this Folder. Folder implementations are expected to use this + * method to broadcast MessageCount events indicating removal of + * messages.

    + * + * The provided implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the registered + * MessageCountListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param removed was the message removed by this client? + * @param msgs the messages that were removed + */ + protected void notifyMessageRemovedListeners(boolean removed, + Message[] msgs) { + if (messageCountListeners == null) + return; + + MessageCountEvent e = new MessageCountEvent( + this, + MessageCountEvent.REMOVED, + removed, + msgs); + queueEvent(e, messageCountListeners); + } + + // Vector of MessageChanged listeners. + private volatile Vector messageChangedListeners + = null; + + /** + * Add a listener for MessageChanged events on this Folder.

    + * + * The implementation provided here adds this listener + * to an internal list of MessageChangedListeners. + * + * @param l the Listener for MessageChanged events + * @see javax.mail.event.MessageChangedEvent + */ + public synchronized void + addMessageChangedListener(MessageChangedListener l) { + if (messageChangedListeners == null) + messageChangedListeners = new Vector<>(); + messageChangedListeners.addElement(l); + } + + /** + * Remove a MessageChanged listener.

    + * + * The implementation provided here removes this listener + * from the internal list of MessageChangedListeners. + * + * @param l the listener + * @see #addMessageChangedListener + */ + public synchronized void + removeMessageChangedListener(MessageChangedListener l) { + if (messageChangedListeners != null) + messageChangedListeners.removeElement(l); + } + + /** + * Notify all MessageChangedListeners. Folder implementations are + * expected to use this method to broadcast MessageChanged events.

    + * + * The provided implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to registered + * MessageChangedListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param type the MessageChangedEvent type + * @param msg the message that changed + */ + protected void notifyMessageChangedListeners(int type, Message msg) { + if (messageChangedListeners == null) + return; + + MessageChangedEvent e = new MessageChangedEvent(this, type, msg); + queueEvent(e, messageChangedListeners); + } + + /* + * Add the event and vector of listeners to the queue to be delivered. + */ + @SuppressWarnings("unchecked") + private void queueEvent(MailEvent event, + Vector vector) { + /* + * Copy the vector in order to freeze the state of the set + * of EventListeners the event should be delivered to prior + * to delivery. This ensures that any changes made to the + * Vector from a target listener's method during the delivery + * of this event will not take effect until after the event is + * delivered. + */ + Vector v = (Vector)vector.clone(); + q.enqueue(event, v); + } + + @Override + protected void finalize() throws Throwable { + try { + q.terminateQueue(); + } finally { + super.finalize(); + } + } + + /** + * override the default toString(), it will return the String + * from Folder.getFullName() or if that is null, it will use + * the default toString() behavior. + */ + + @Override + public String toString() { + String s = getFullName(); + if (s != null) + return s; + else + return super.toString(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/FolderClosedException.java b/fine-third-default/fine-mail/src/javax/mail/FolderClosedException.java new file mode 100644 index 000000000..cc2b58ac9 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/FolderClosedException.java @@ -0,0 +1,107 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * This exception is thrown when a method is invoked on a Messaging object + * and the Folder that owns that object has died due to some reason.

    + * + * Following the exception, the Folder is reset to the "closed" state. + * All messaging objects owned by the Folder should be considered invalid. + * The Folder can be reopened using the "open" method to reestablish the + * lost connection.

    + * + * The getMessage() method returns more detailed information about the + * error that caused this exception.

    + * + * @author John Mani + */ + +public class FolderClosedException extends MessagingException { + transient private Folder folder; + + private static final long serialVersionUID = 1687879213433302315L; + + /** + * Constructs a FolderClosedException. + * + * @param folder The Folder + */ + public FolderClosedException(Folder folder) { + this(folder, null); + } + + /** + * Constructs a FolderClosedException with the specified + * detail message. + * + * @param folder The Folder + * @param message The detailed error message + */ + public FolderClosedException(Folder folder, String message) { + super(message); + this.folder = folder; + } + + /** + * Constructs a FolderClosedException with the specified + * detail message and embedded exception. The exception is chained + * to this exception. + * + * @param folder The Folder + * @param message The detailed error message + * @param e The embedded exception + * @since JavaMail 1.5 + */ + public FolderClosedException(Folder folder, String message, Exception e) { + super(message, e); + this.folder = folder; + } + + /** + * Returns the dead Folder object + * + * @return the dead Folder object + */ + public Folder getFolder() { + return folder; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/FolderNotFoundException.java b/fine-third-default/fine-mail/src/javax/mail/FolderNotFoundException.java new file mode 100644 index 000000000..6d46abd39 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/FolderNotFoundException.java @@ -0,0 +1,124 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.lang.*; + +/** + * This exception is thrown by Folder methods, when those + * methods are invoked on a non existent folder. + * + * @author John Mani + */ + +public class FolderNotFoundException extends MessagingException { + transient private Folder folder; + + private static final long serialVersionUID = 472612108891249403L; + + /** + * Constructs a FolderNotFoundException with no detail message. + */ + public FolderNotFoundException() { + super(); + } + + /** + * Constructs a FolderNotFoundException. + * + * @param folder The Folder + * @since JavaMail 1.2 + */ + public FolderNotFoundException(Folder folder) { + super(); + this.folder = folder; + } + + /** + * Constructs a FolderNotFoundException with the specified + * detail message. + * + * @param folder The Folder + * @param s The detailed error message + * @since JavaMail 1.2 + */ + public FolderNotFoundException(Folder folder, String s) { + super(s); + this.folder = folder; + } + + /** + * Constructs a FolderNotFoundException with the specified + * detail message and embedded exception. The exception is chained + * to this exception. + * + * @param folder The Folder + * @param s The detailed error message + * @param e The embedded exception + * @since JavaMail 1.5 + */ + public FolderNotFoundException(Folder folder, String s, Exception e) { + super(s, e); + this.folder = folder; + } + + /** + * Constructs a FolderNotFoundException with the specified detail message + * and the specified folder. + * + * @param s The detail message + * @param folder The Folder + */ + public FolderNotFoundException(String s, Folder folder) { + super(s); + this.folder = folder; + } + + /** + * Returns the offending Folder object. + * + * @return the Folder object. Note that the returned value can be + * null. + */ + public Folder getFolder() { + return folder; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Header.java b/fine-third-default/fine-mail/src/javax/mail/Header.java new file mode 100644 index 000000000..ad3c625fb --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Header.java @@ -0,0 +1,94 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + + +/** + * The Header class stores a name/value pair to represent headers. + * + * @author John Mani + */ + +public class Header { + + /** + * The name of the header. + * + * @since JavaMail 1.4 + */ + protected String name; + + /** + * The value of the header. + * + * @since JavaMail 1.4 + */ + protected String value; + + /** + * Construct a Header object. + * + * @param name name of the header + * @param value value of the header + */ + public Header(String name, String value) { + this.name = name; + this.value = value; + } + + /** + * Returns the name of this header. + * + * @return name of the header + */ + public String getName() { + return name; + } + + /** + * Returns the value of this header. + * + * @return value of the header + */ + public String getValue() { + return value; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/IllegalWriteException.java b/fine-third-default/fine-mail/src/javax/mail/IllegalWriteException.java new file mode 100644 index 000000000..762e392f9 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/IllegalWriteException.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + + +/** + * The exception thrown when a write is attempted on a read-only attribute + * of any Messaging object. + * + * @author John Mani + */ + +public class IllegalWriteException extends MessagingException { + + private static final long serialVersionUID = 3974370223328268013L; + + /** + * Constructs an IllegalWriteException with no detail message. + */ + public IllegalWriteException() { + super(); + } + + /** + * Constructs an IllegalWriteException with the specified + * detail message. + * + * @param s The detailed error message + */ + public IllegalWriteException(String s) { + super(s); + } + + /** + * Constructs an IllegalWriteException with the specified + * detail message and embedded exception. The exception is chained + * to this exception. + * + * @param s The detailed error message + * @param e The embedded exception + * @since JavaMail 1.5 + */ + public IllegalWriteException(String s, Exception e) { + super(s, e); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/MailSessionDefinition.java b/fine-third-default/fine-mail/src/javax/mail/MailSessionDefinition.java new file mode 100644 index 000000000..2cdf97512 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/MailSessionDefinition.java @@ -0,0 +1,132 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.Repeatable; + +/** + * Annotation used by Java EE applications to define a MailSession + * to be registered with JNDI. The MailSession may be configured + * by setting the annotation elements for commonly used Session + * properties. Additional standard and vendor-specific properties may be + * specified using the properties element. + *

    + * The session will be registered under the name specified in the + * name element. It may be defined to be in any valid + * Java EE namespace, and will determine the accessibility of + * the session from other components. + * + * @since JavaMail 1.5 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Repeatable(MailSessionDefinitions.class) +public @interface MailSessionDefinition { + + /** + * Description of this mail session. + * + * @return the description + */ + String description() default ""; + + /** + * JNDI name by which the mail session will be registered. + * + * @return the JNDI name + */ + String name(); + + /** + * Store protocol name. + * + * @return the store protocol name + */ + String storeProtocol() default ""; + + /** + * Transport protocol name. + * + * @return the transport protocol name + */ + String transportProtocol() default ""; + + /** + * Host name for the mail server. + * + * @return the host name + */ + String host() default ""; + + /** + * User name to use for authentication. + * + * @return the user name + */ + String user() default ""; + + /** + * Password to use for authentication. + * + * @return the password + */ + String password() default ""; + + /** + * From address for the user. + * + * @return the from address + */ + String from() default ""; + + /** + * Properties to include in the Session. + * Properties are specified using the format: + * propertyName=propertyValue with one property per array element. + * + * @return the properties + */ + String[] properties() default {}; +} diff --git a/fine-third-default/fine-mail/src/javax/mail/MailSessionDefinitions.java b/fine-third-default/fine-mail/src/javax/mail/MailSessionDefinitions.java new file mode 100644 index 000000000..e6cb46f03 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/MailSessionDefinitions.java @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; + +/** + * Declares one or more MailSessionDefinition annotations. + * + * @see MailSessionDefinition + * @since JavaMail 1.5 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface MailSessionDefinitions { + MailSessionDefinition[] value(); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Message.java b/fine-third-default/fine-mail/src/javax/mail/Message.java new file mode 100644 index 000000000..254e43b98 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Message.java @@ -0,0 +1,728 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.util.Date; +import java.io.*; +import javax.mail.search.SearchTerm; + +/** + * This class models an email message. This is an abstract class. + * Subclasses provide actual implementations.

    + * + * Message implements the Part interface. Message contains a set of + * attributes and a "content". Messages within a folder also have a + * set of flags that describe its state within the folder.

    + * + * Message defines some new attributes in addition to those defined + * in the Part interface. These attributes specify meta-data + * for the message - i.e., addressing and descriptive information about + * the message.

    + * + * Message objects are obtained either from a Folder or by constructing + * a new Message object of the appropriate subclass. Messages that have + * been received are normally retrieved from a folder named "INBOX".

    + * + * A Message object obtained from a folder is just a lightweight + * reference to the actual message. The Message is 'lazily' filled + * up (on demand) when each item is requested from the message. Note + * that certain folder implementations may return Message objects that + * are pre-filled with certain user-specified items. + + * To send a message, an appropriate subclass of Message (e.g., + * MimeMessage) is instantiated, the attributes and content are + * filled in, and the message is sent using the Transport.send + * method.

    + * + * @author John Mani + * @author Bill Shannon + * @author Max Spivak + * @see javax.mail.Part + */ + +public abstract class Message implements Part { + + /** + * The number of this message within its folder, or zero if + * the message was not retrieved from a folder. + */ + protected int msgnum = 0; + + /** + * True if this message has been expunged. + */ + protected boolean expunged = false; + + /** + * The containing folder, if this message is obtained from a folder + */ + protected Folder folder = null; + + /** + * The Session object for this Message + */ + protected Session session = null; + + /** + * No-arg version of the constructor. + */ + protected Message() { } + + /** + * Constructor that takes a Folder and a message number. + * Used by Folder implementations. + * + * @param folder containing folder + * @param msgnum this message's sequence number within this folder + */ + protected Message(Folder folder, int msgnum) { + this.folder = folder; + this.msgnum = msgnum; + session = folder.store.session; + } + + /** + * Constructor that takes a Session. Used for client created + * Message objects. + * + * @param session A Session object + */ + protected Message(Session session) { + this.session = session; + } + + /** + * Return the Session used when this message was created. + * + * @return the message's Session + * @since JavaMail 1.5 + */ + public Session getSession() { + return session; + } + + /** + * Returns the "From" attribute. The "From" attribute contains + * the identity of the person(s) who wished this message to + * be sent.

    + * + * In certain implementations, this may be different + * from the entity that actually sent the message.

    + * + * This method returns null if this attribute + * is not present in this message. Returns an empty array if + * this attribute is present, but contains no addresses. + * + * @return array of Address objects + * @exception MessagingException for failures + */ + public abstract Address[] getFrom() throws MessagingException; + + /** + * Set the "From" attribute in this Message. The value of this + * attribute is obtained from the property "mail.user". If this + * property is absent, the system property "user.name" is used. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + public abstract void setFrom() throws MessagingException; + + /** + * Set the "From" attribute in this Message. + * + * @param address the sender + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + public abstract void setFrom(Address address) + throws MessagingException; + + /** + * Add these addresses to the existing "From" attribute + * + * @param addresses the senders + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + public abstract void addFrom(Address[] addresses) + throws MessagingException; + + /** + * This inner class defines the types of recipients allowed by + * the Message class. The currently defined types are TO, + * CC and BCC. + * + * Note that this class only has a protected constructor, thereby + * restricting new Recipient types to either this class or subclasses. + * This effectively implements an enumeration of the allowed Recipient + * types. + * + * The following code sample shows how to use this class to obtain + * the "TO" recipients from a message. + *

    +     *
    +     * Message msg = folder.getMessages(1);
    +     * Address[] a = m.getRecipients(Message.RecipientType.TO);
    +     *
    +     * 
    + * + * @see javax.mail.Message#getRecipients + * @see javax.mail.Message#setRecipients + * @see javax.mail.Message#addRecipients + */ + public static class RecipientType implements Serializable { + /** + * The "To" (primary) recipients. + */ + public static final RecipientType TO = new RecipientType("To"); + /** + * The "Cc" (carbon copy) recipients. + */ + public static final RecipientType CC = new RecipientType("Cc"); + /** + * The "Bcc" (blind carbon copy) recipients. + */ + public static final RecipientType BCC = new RecipientType("Bcc"); + + /** + * The type of recipient, usually the name of a corresponding + * Internet standard header. + * + * @serial + */ + protected String type; + + private static final long serialVersionUID = -7479791750606340008L; + + /** + * Constructor for use by subclasses. + * + * @param type the recipient type + */ + protected RecipientType(String type) { + this.type = type; + } + + /** + * When deserializing a RecipientType, we need to make sure to + * return only one of the known static final instances defined + * in this class. Subclasses must implement their own + * readResolve method that checks for their known + * instances before calling this super method. + * + * @return the RecipientType object instance + * @exception ObjectStreamException for object stream errors + */ + protected Object readResolve() throws ObjectStreamException { + if (type.equals("To")) + return TO; + else if (type.equals("Cc")) + return CC; + else if (type.equals("Bcc")) + return BCC; + else + throw new InvalidObjectException( + "Attempt to resolve unknown RecipientType: " + type); + } + + @Override + public String toString() { + return type; + } + } + + /** + * Get all the recipient addresses of the given type.

    + * + * This method returns null if no recipients of + * the given type are present in this message. It may return an + * empty array if the header is present, but contains no addresses. + * + * @param type the recipient type + * @return array of Address objects + * @exception MessagingException for failures + * @see Message.RecipientType#TO + * @see Message.RecipientType#CC + * @see Message.RecipientType#BCC + */ + public abstract Address[] getRecipients(RecipientType type) + throws MessagingException; + + /** + * Get all the recipient addresses for the message. + * The default implementation extracts the TO, CC, and BCC + * recipients using the getRecipients method.

    + * + * This method returns null if none of the recipient + * headers are present in this message. It may Return an empty array + * if any recipient header is present, but contains no addresses. + * + * @return array of Address objects + * @exception MessagingException for failures + * @see Message.RecipientType#TO + * @see Message.RecipientType#CC + * @see Message.RecipientType#BCC + * @see #getRecipients + */ + public Address[] getAllRecipients() throws MessagingException { + Address[] to = getRecipients(RecipientType.TO); + Address[] cc = getRecipients(RecipientType.CC); + Address[] bcc = getRecipients(RecipientType.BCC); + + if (cc == null && bcc == null) + return to; // a common case + + int numRecip = + (to != null ? to.length : 0) + + (cc != null ? cc.length : 0) + + (bcc != null ? bcc.length : 0); + Address[] addresses = new Address[numRecip]; + int pos = 0; + if (to != null) { + System.arraycopy(to, 0, addresses, pos, to.length); + pos += to.length; + } + if (cc != null) { + System.arraycopy(cc, 0, addresses, pos, cc.length); + pos += cc.length; + } + if (bcc != null) { + System.arraycopy(bcc, 0, addresses, pos, bcc.length); + // pos += bcc.length; + } + return addresses; + } + + /** + * Set the recipient addresses. All addresses of the specified + * type are replaced by the addresses parameter. + * + * @param type the recipient type + * @param addresses the addresses + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + public abstract void setRecipients(RecipientType type, Address[] addresses) + throws MessagingException; + + /** + * Set the recipient address. All addresses of the specified + * type are replaced by the address parameter.

    + * + * The default implementation uses the setRecipients method. + * + * @param type the recipient type + * @param address the address + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception MessagingException for other failures + */ + public void setRecipient(RecipientType type, Address address) + throws MessagingException { + if (address == null) + setRecipients(type, null); + else { + Address[] a = new Address[1]; + a[0] = address; + setRecipients(type, a); + } + } + + /** + * Add these recipient addresses to the existing ones of the given type. + * + * @param type the recipient type + * @param addresses the addresses + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + public abstract void addRecipients(RecipientType type, Address[] addresses) + throws MessagingException; + + /** + * Add this recipient address to the existing ones of the given type.

    + * + * The default implementation uses the addRecipients method. + * + * @param type the recipient type + * @param address the address + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception MessagingException for other failures + */ + public void addRecipient(RecipientType type, Address address) + throws MessagingException { + Address[] a = new Address[1]; + a[0] = address; + addRecipients(type, a); + } + + /** + * Get the addresses to which replies should be directed. + * This will usually be the sender of the message, but + * some messages may direct replies to a different address.

    + * + * The default implementation simply calls the getFrom + * method.

    + * + * This method returns null if the corresponding + * header is not present. Returns an empty array if the header + * is present, but contains no addresses. + * + * @return addresses to which replies should be directed + * @exception MessagingException for failures + * @see #getFrom + */ + public Address[] getReplyTo() throws MessagingException { + return getFrom(); + } + + /** + * Set the addresses to which replies should be directed. + * (Normally only a single address will be specified.) + * Not all message types allow this to be specified separately + * from the sender of the message.

    + * + * The default implementation provided here just throws the + * MethodNotSupportedException. + * + * @param addresses addresses to which replies should be directed + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MethodNotSupportedException if the underlying + * implementation does not support setting this + * attribute + * @exception MessagingException for other failures + */ + public void setReplyTo(Address[] addresses) throws MessagingException { + throw new MethodNotSupportedException("setReplyTo not supported"); + } + + /** + * Get the subject of this message. + * + * @return the subject + * @exception MessagingException for failures + */ + public abstract String getSubject() throws MessagingException; + + /** + * Set the subject of this message. + * + * @param subject the subject + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + public abstract void setSubject(String subject) + throws MessagingException; + + /** + * Get the date this message was sent. + * + * @return the date this message was sent + * @exception MessagingException for failures + */ + public abstract Date getSentDate() throws MessagingException; + + /** + * Set the sent date of this message. + * + * @param date the sent date of this message + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + public abstract void setSentDate(Date date) throws MessagingException; + + /** + * Get the date this message was received. + * + * @return the date this message was received + * @exception MessagingException for failures + */ + public abstract Date getReceivedDate() throws MessagingException; + + /** + * Returns a Flags object containing the flags for + * this message.

    + * + * Modifying any of the flags in this returned Flags object will + * not affect the flags of this message. Use setFlags() + * to do that.

    + * + * @return Flags object containing the flags for this message + * @see javax.mail.Flags + * @see #setFlags + * @exception MessagingException for failures + */ + public abstract Flags getFlags() throws MessagingException; + + /** + * Check whether the flag specified in the flag + * argument is set in this message.

    + * + * The default implementation uses getFlags. + * + * @param flag the flag + * @return value of the specified flag for this message + * @see javax.mail.Flags.Flag + * @see javax.mail.Flags.Flag#ANSWERED + * @see javax.mail.Flags.Flag#DELETED + * @see javax.mail.Flags.Flag#DRAFT + * @see javax.mail.Flags.Flag#FLAGGED + * @see javax.mail.Flags.Flag#RECENT + * @see javax.mail.Flags.Flag#SEEN + * @exception MessagingException for failures + */ + public boolean isSet(Flags.Flag flag) throws MessagingException { + return getFlags().contains(flag); + } + + /** + * Set the specified flags on this message to the specified value. + * Note that any flags in this message that are not specified in + * the given Flags object are unaffected.

    + * + * This will result in a MessageChangedEvent being + * delivered to any MessageChangedListener registered on this + * Message's containing folder. + * + * @param flag Flags object containing the flags to be set + * @param set the value to be set + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values. + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + * @see javax.mail.event.MessageChangedEvent + */ + public abstract void setFlags(Flags flag, boolean set) + throws MessagingException; + + /** + * Set the specified flag on this message to the specified value. + * + * This will result in a MessageChangedEvent being + * delivered to any MessageChangedListener registered on this + * Message's containing folder.

    + * + * The default implementation uses the setFlags method. + * + * @param flag Flags.Flag object containing the flag to be set + * @param set the value to be set + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values. + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + * @see javax.mail.event.MessageChangedEvent + */ + public void setFlag(Flags.Flag flag, boolean set) + throws MessagingException { + Flags f = new Flags(flag); + setFlags(f, set); + } + + /** + * Get the Message number for this Message. + * A Message object's message number is the relative + * position of this Message in its Folder. Note that the message + * number for a particular Message can change during a session + * if other messages in the Folder are deleted and expunged.

    + * + * Valid message numbers start at 1. Messages that do not belong + * to any folder (like newly composed or derived messages) have 0 + * as their message number. + * + * @return the message number + */ + public int getMessageNumber() { + return msgnum; + } + + /** + * Set the Message number for this Message. This method is + * invoked only by the implementation classes. + * + * @param msgnum the message number + */ + protected void setMessageNumber(int msgnum) { + this.msgnum = msgnum; + } + + /** + * Get the folder from which this message was obtained. If + * this is a new message or nested message, this method returns + * null. + * + * @return the containing folder + */ + public Folder getFolder() { + return folder; + } + + /** + * Checks whether this message is expunged. All other methods except + * getMessageNumber() are invalid on an expunged + * Message object.

    + * + * Messages that are expunged due to an explict expunge() + * request on the containing Folder are removed from the Folder + * immediately. Messages that are externally expunged by another source + * are marked "expunged" and return true for the isExpunged() method, + * but they are not removed from the Folder until an explicit + * expunge() is done on the Folder.

    + * + * See the description of expunge() for more details on + * expunge handling. + * + * @return true if the message is expunged + * @see Folder#expunge + */ + public boolean isExpunged() { + return expunged; + } + + /** + * Sets the expunged flag for this Message. This method is to + * be used only by the implementation classes. + * + * @param expunged the expunged flag + */ + protected void setExpunged(boolean expunged) { + this.expunged = expunged; + } + + /** + * Get a new Message suitable for a reply to this message. + * The new Message will have its attributes and headers + * set up appropriately. Note that this new message object + * will be empty, that is, it will not have a "content". + * These will have to be suitably filled in by the client.

    + * + * If replyToAll is set, the new Message will be addressed + * to all recipients of this message. Otherwise, the reply will be + * addressed to only the sender of this message (using the value + * of the getReplyTo method).

    + * + * The "Subject" field is filled in with the original subject + * prefixed with "Re:" (unless it already starts with "Re:").

    + * + * The reply message will use the same session as this message. + * + * @param replyToAll reply should be sent to all recipients + * of this message + * @return the reply Message + * @exception MessagingException for failures + */ + public abstract Message reply(boolean replyToAll) throws MessagingException; + + /** + * Save any changes made to this message into the message-store + * when the containing folder is closed, if the message is contained + * in a folder. (Some implementations may save the changes + * immediately.) Update any header fields to be consistent with the + * changed message contents. If any part of a message's headers or + * contents are changed, saveChanges must be called to ensure that + * those changes are permanent. If saveChanges is not called, any + * such modifications may or may not be saved, depending on the + * message store and folder implementation.

    + * + * Messages obtained from folders opened READ_ONLY should not be + * modified and saveChanges should not be called on such messages. + * + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values. + * @exception MessagingException for other failures + */ + public abstract void saveChanges() throws MessagingException; + + /** + * Apply the specified Search criterion to this message. + * + * @param term the Search criterion + * @return true if the Message matches this search + * criterion, false otherwise. + * @exception MessagingException for failures + * @see javax.mail.search.SearchTerm + */ + public boolean match(SearchTerm term) throws MessagingException { + return term.match(this); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/MessageAware.java b/fine-third-default/fine-mail/src/javax/mail/MessageAware.java new file mode 100644 index 000000000..f48b67120 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/MessageAware.java @@ -0,0 +1,60 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * An interface optionally implemented by DataSources to + * supply information to a DataContentHandler about the + * message context in which the data content object is operating. + * + * @see javax.mail.MessageContext + * @see javax.activation.DataSource + * @see javax.activation.DataContentHandler + * @since JavaMail 1.1 + */ +public interface MessageAware { + /** + * Return the message context. + * + * @return the message context + */ + public MessageContext getMessageContext(); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/MessageContext.java b/fine-third-default/fine-mail/src/javax/mail/MessageContext.java new file mode 100644 index 000000000..b7247ea8b --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/MessageContext.java @@ -0,0 +1,124 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * The context in which a piece of Message content is contained. A + * MessageContext object is returned by the + * getMessageContext method of the + * MessageAware interface. MessageAware is + * typically implemented by DataSources to allow a + * DataContentHandler to pass on information about the + * context in which a data content object is operating. + * + * @see javax.mail.MessageAware + * @see javax.activation.DataSource + * @see javax.activation.DataContentHandler + * @since JavaMail 1.1 + */ +public class MessageContext { + private Part part; + + /** + * Create a MessageContext object describing the context of the given Part. + * + * @param part the Part + */ + public MessageContext(Part part) { + this.part = part; + } + + /** + * Return the Part that contains the content. + * + * @return the containing Part, or null if not known + */ + public Part getPart() { + return part; + } + + /** + * Return the Message that contains the content. + * Follows the parent chain up through containing Multipart + * objects until it comes to a Message object, or null. + * + * @return the containing Message, or null if not known + */ + public Message getMessage() { + try { + return getMessage(part); + } catch (MessagingException ex) { + return null; + } + } + + /** + * Return the Message containing an arbitrary Part. + * Follows the parent chain up through containing Multipart + * objects until it comes to a Message object, or null. + * + * @return the containing Message, or null if none + * @see javax.mail.BodyPart#getParent + * @see javax.mail.Multipart#getParent + */ + private static Message getMessage(Part p) throws MessagingException { + while (p != null) { + if (p instanceof Message) + return (Message)p; + BodyPart bp = (BodyPart)p; + Multipart mp = bp.getParent(); + if (mp == null) // MimeBodyPart might not be in a MimeMultipart + return null; + p = mp.getParent(); + } + return null; + } + + /** + * Return the Session we're operating in. + * + * @return the Session, or null if not known + */ + public Session getSession() { + Message msg = getMessage(); + return msg != null ? msg.getSession() : null; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/MessageRemovedException.java b/fine-third-default/fine-mail/src/javax/mail/MessageRemovedException.java new file mode 100644 index 000000000..edad8dd8c --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/MessageRemovedException.java @@ -0,0 +1,86 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * The exception thrown when an invalid method is invoked on an expunged + * Message. The only valid methods on an expunged Message are + * isExpunged() and getMessageNumber(). + * + * @see javax.mail.Message#isExpunged() + * @see javax.mail.Message#getMessageNumber() + * @author John Mani + */ + +public class MessageRemovedException extends MessagingException { + + private static final long serialVersionUID = 1951292550679528690L; + + /** + * Constructs a MessageRemovedException with no detail message. + */ + public MessageRemovedException() { + super(); + } + + /** + * Constructs a MessageRemovedException with the specified + * detail message. + * + * @param s The detailed error message + */ + public MessageRemovedException(String s) { + super(s); + } + + /** + * Constructs a MessageRemovedException with the specified + * detail message and embedded exception. The exception is chained + * to this exception. + * + * @param s The detailed error message + * @param e The embedded exception + * @since JavaMail 1.5 + */ + public MessageRemovedException(String s, Exception e) { + super(s, e); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/MessagingException.java b/fine-third-default/fine-mail/src/javax/mail/MessagingException.java new file mode 100644 index 000000000..3212e6b2f --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/MessagingException.java @@ -0,0 +1,176 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.lang.*; + +/** + * The base class for all exceptions thrown by the Messaging classes + * + * @author John Mani + * @author Bill Shannon + */ + +public class MessagingException extends Exception { + + /** + * The next exception in the chain. + * + * @serial + */ + private Exception next; + + private static final long serialVersionUID = -7569192289819959253L; + + /** + * Constructs a MessagingException with no detail message. + */ + public MessagingException() { + super(); + initCause(null); // prevent anyone else from setting it + } + + /** + * Constructs a MessagingException with the specified detail message. + * + * @param s the detail message + */ + public MessagingException(String s) { + super(s); + initCause(null); // prevent anyone else from setting it + } + + /** + * Constructs a MessagingException with the specified + * Exception and detail message. The specified exception is chained + * to this exception. + * + * @param s the detail message + * @param e the embedded exception + * @see #getNextException + * @see #setNextException + * @see #getCause + */ + public MessagingException(String s, Exception e) { + super(s); + next = e; + initCause(null); // prevent anyone else from setting it + } + + /** + * Get the next exception chained to this one. If the + * next exception is a MessagingException, the chain + * may extend further. + * + * @return next Exception, null if none. + */ + public synchronized Exception getNextException() { + return next; + } + + /** + * Overrides the getCause method of Throwable + * to return the next exception in the chain of nested exceptions. + * + * @return next Exception, null if none. + */ + @Override + public synchronized Throwable getCause() { + return next; + } + + /** + * Add an exception to the end of the chain. If the end + * is not a MessagingException, this + * exception cannot be added to the end. + * + * @param ex the new end of the Exception chain + * @return true if this Exception + * was added, false otherwise. + */ + public synchronized boolean setNextException(Exception ex) { + Exception theEnd = this; + while (theEnd instanceof MessagingException && + ((MessagingException)theEnd).next != null) { + theEnd = ((MessagingException)theEnd).next; + } + // If the end is a MessagingException, we can add this + // exception to the chain. + if (theEnd instanceof MessagingException) { + ((MessagingException)theEnd).next = ex; + return true; + } else + return false; + } + + /** + * Override toString method to provide information on + * nested exceptions. + */ + @Override + public synchronized String toString() { + String s = super.toString(); + Exception n = next; + if (n == null) + return s; + StringBuilder sb = new StringBuilder(s == null ? "" : s); + while (n != null) { + sb.append(";\n nested exception is:\n\t"); + if (n instanceof MessagingException) { + MessagingException mex = (MessagingException)n; + sb.append(mex.superToString()); + n = mex.next; + } else { + sb.append(n.toString()); + n = null; + } + } + return sb.toString(); + } + + /** + * Return the "toString" information for this exception, + * without any information on nested exceptions. + */ + private final String superToString() { + return super.toString(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/MethodNotSupportedException.java b/fine-third-default/fine-mail/src/javax/mail/MethodNotSupportedException.java new file mode 100644 index 000000000..c1f360e62 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/MethodNotSupportedException.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + + +/** + * The exception thrown when a method is not supported by the + * implementation + * + * @author John Mani + */ + +public class MethodNotSupportedException extends MessagingException { + + private static final long serialVersionUID = -3757386618726131322L; + + /** + * Constructs a MethodNotSupportedException with no detail message. + */ + public MethodNotSupportedException() { + super(); + } + + /** + * Constructs a MethodNotSupportedException with the specified + * detail message. + * + * @param s The detailed error message + */ + public MethodNotSupportedException(String s) { + super(s); + } + + /** + * Constructs a MethodNotSupportedException with the specified + * detail message and embedded exception. The exception is chained + * to this exception. + * + * @param s The detailed error message + * @param e The embedded exception + * @since JavaMail 1.5 + */ + public MethodNotSupportedException(String s, Exception e) { + super(s, e); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Multipart.java b/fine-third-default/fine-mail/src/javax/mail/Multipart.java new file mode 100644 index 000000000..0c99a6f0b --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Multipart.java @@ -0,0 +1,284 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.util.Vector; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import javax.activation.DataSource; + +/** + * Multipart is a container that holds multiple body parts. Multipart + * provides methods to retrieve and set its subparts.

    + * + * Multipart also acts as the base class for the content object returned + * by most Multipart DataContentHandlers. For example, invoking getContent() + * on a DataHandler whose source is a "multipart/signed" data source may + * return an appropriate subclass of Multipart.

    + * + * Some messaging systems provide different subtypes of Multiparts. For + * example, MIME specifies a set of subtypes that include "alternative", + * "mixed", "related", "parallel", "signed", etc.

    + * + * Multipart is an abstract class. Subclasses provide actual implementations. + * + * @author John Mani + */ + +public abstract class Multipart { + + /** + * Vector of BodyPart objects. + */ + protected Vector parts = new Vector<>(); // Holds BodyParts + + /** + * This field specifies the content-type of this multipart + * object. It defaults to "multipart/mixed". + */ + protected String contentType = "multipart/mixed"; // Content-Type + + /** + * The Part containing this Multipart, + * if known. + * @since JavaMail 1.1 + */ + protected Part parent; + + /** + * Default constructor. An empty Multipart object is created. + */ + protected Multipart() { } + + /** + * Setup this Multipart object from the given MultipartDataSource.

    + * + * The method adds the MultipartDataSource's BodyPart + * objects into this Multipart. This Multipart's contentType is + * set to that of the MultipartDataSource.

    + * + * This method is typically used in those cases where one + * has a multipart data source that has already been pre-parsed into + * the individual body parts (for example, an IMAP datasource), but + * needs to create an appropriate Multipart subclass that represents + * a specific multipart subtype. + * + * @param mp Multipart datasource + * @exception MessagingException for failures + */ + protected synchronized void setMultipartDataSource(MultipartDataSource mp) + throws MessagingException { + contentType = mp.getContentType(); + + int count = mp.getCount(); + for (int i = 0; i < count; i++) + addBodyPart(mp.getBodyPart(i)); + } + + /** + * Return the content-type of this Multipart.

    + * + * This implementation just returns the value of the + * contentType field. + * + * @return content-type + * @see #contentType + */ + public synchronized String getContentType() { + return contentType; + } + + /** + * Return the number of enclosed BodyPart objects.

    + * + * @return number of parts + * @exception MessagingException for failures + * @see #parts + */ + public synchronized int getCount() throws MessagingException { + if (parts == null) + return 0; + + return parts.size(); + } + + /** + * Get the specified Part. Parts are numbered starting at 0. + * + * @param index the index of the desired Part + * @return the Part + * @exception IndexOutOfBoundsException if the given index + * is out of range. + * @exception MessagingException for other failures + */ + public synchronized BodyPart getBodyPart(int index) + throws MessagingException { + if (parts == null) + throw new IndexOutOfBoundsException("No such BodyPart"); + + return parts.elementAt(index); + } + + /** + * Remove the specified part from the multipart message. + * Shifts all the parts after the removed part down one. + * + * @param part The part to remove + * @return true if part removed, false otherwise + * @exception MessagingException if no such Part exists + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + */ + public synchronized boolean removeBodyPart(BodyPart part) + throws MessagingException { + if (parts == null) + throw new MessagingException("No such body part"); + + boolean ret = parts.removeElement(part); + part.setParent(null); + return ret; + } + + /** + * Remove the part at specified location (starting from 0). + * Shifts all the parts after the removed part down one. + * + * @param index Index of the part to remove + * @exception IndexOutOfBoundsException if the given index + * is out of range. + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception MessagingException for other failures + */ + public synchronized void removeBodyPart(int index) + throws MessagingException { + if (parts == null) + throw new IndexOutOfBoundsException("No such BodyPart"); + + BodyPart part = parts.elementAt(index); + parts.removeElementAt(index); + part.setParent(null); + } + + /** + * Adds a Part to the multipart. The BodyPart is appended to + * the list of existing Parts. + * + * @param part The Part to be appended + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception MessagingException for other failures + */ + public synchronized void addBodyPart(BodyPart part) + throws MessagingException { + if (parts == null) + parts = new Vector<>(); + + parts.addElement(part); + part.setParent(this); + } + + /** + * Adds a BodyPart at position index. + * If index is not the last one in the list, + * the subsequent parts are shifted up. If index + * is larger than the number of parts present, the + * BodyPart is appended to the end. + * + * @param part The BodyPart to be inserted + * @param index Location where to insert the part + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception MessagingException for other failures + */ + public synchronized void addBodyPart(BodyPart part, int index) + throws MessagingException { + if (parts == null) + parts = new Vector<>(); + + parts.insertElementAt(part, index); + part.setParent(this); + } + + /** + * Output an appropriately encoded bytestream to the given + * OutputStream. The implementation subclass decides the + * appropriate encoding algorithm to be used. The bytestream + * is typically used for sending. + * + * @param os the stream to write to + * @exception IOException if an IO related exception occurs + * @exception MessagingException for other failures + */ + public abstract void writeTo(OutputStream os) + throws IOException, MessagingException; + + /** + * Return the Part that contains this Multipart + * object, or null if not known. + * + * @return the parent Part + * @since JavaMail 1.1 + */ + public synchronized Part getParent() { + return parent; + } + + /** + * Set the parent of this Multipart to be the specified + * Part. Normally called by the Message + * or BodyPart setContent(Multipart) method. + * parent may be null if the + * Multipart is being removed from its containing + * Part. + * + * @param parent the parent Part + * @since JavaMail 1.1 + */ + public synchronized void setParent(Part parent) { + this.parent = parent; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/MultipartDataSource.java b/fine-third-default/fine-mail/src/javax/mail/MultipartDataSource.java new file mode 100644 index 000000000..1eb2cf19d --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/MultipartDataSource.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import javax.activation.DataSource; + +/** + * MultipartDataSource is a DataSource that contains body + * parts. This allows "mail aware" DataContentHandlers to + * be implemented more efficiently by being aware of such + * DataSources and using the appropriate methods to access + * BodyParts.

    + * + * Note that the data of a MultipartDataSource is also available as + * an input stream.

    + * + * This interface will typically be implemented by providers that + * preparse multipart bodies, for example an IMAP provider. + * + * @author John Mani + * @see javax.activation.DataSource + */ + +public interface MultipartDataSource extends DataSource { + + /** + * Return the number of enclosed BodyPart objects. + * + * @return number of parts + */ + public int getCount(); + + /** + * Get the specified Part. Parts are numbered starting at 0. + * + * @param index the index of the desired Part + * @return the Part + * @exception IndexOutOfBoundsException if the given index + * is out of range. + * @exception MessagingException for other failures + */ + public BodyPart getBodyPart(int index) throws MessagingException; + +} diff --git a/fine-third-default/fine-mail/src/javax/mail/NoSuchProviderException.java b/fine-third-default/fine-mail/src/javax/mail/NoSuchProviderException.java new file mode 100644 index 000000000..6794d9c9b --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/NoSuchProviderException.java @@ -0,0 +1,83 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * This exception is thrown when Session attempts to instantiate a + * Provider that doesn't exist. + * + * @author Max Spivak + */ + +public class NoSuchProviderException extends MessagingException { + + private static final long serialVersionUID = 8058319293154708827L; + + /** + * Constructs a NoSuchProviderException with no detail message. + */ + public NoSuchProviderException() { + super(); + } + + /** + * Constructs a NoSuchProviderException with the specified + * detail message. + * + * @param message The detailed error message + */ + public NoSuchProviderException(String message) { + super(message); + } + + /** + * Constructs a NoSuchProviderException with the specified + * detail message and embedded exception. The exception is chained + * to this exception. + * + * @param message The detailed error message + * @param e The embedded exception + * @since JavaMail 1.5 + */ + public NoSuchProviderException(String message, Exception e) { + super(message, e); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Part.java b/fine-third-default/fine-mail/src/javax/mail/Part.java new file mode 100644 index 000000000..10727f211 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Part.java @@ -0,0 +1,475 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.io.*; +import java.util.Enumeration; +import javax.activation.DataHandler; + +/** + * The Part interface is the common base interface for + * Messages and BodyParts.

    + * + * Part consists of a set of attributes and a "Content".

    + * + * Attributes:

    + * + * The JavaMail API defines a set of standard Part attributes that are + * considered to be common to most existing Mail systems. These + * attributes have their own settor and gettor methods. Mail systems + * may support other Part attributes as well, these are represented as + * name-value pairs where both the name and value are Strings.

    + * + * Content:

    + * + * The data type of the "content" is returned by + * the getContentType() method. The MIME typing system + * is used to name data types.

    + * + * The "content" of a Part is available in various formats: + *

      + *
    • As a DataHandler - using the getDataHandler() method. + * The "content" of a Part is also available through a + * javax.activation.DataHandler object. The DataHandler + * object allows clients to discover the operations available on the + * content, and to instantiate the appropriate component to perform + * those operations. + * + *
    • As an input stream - using the getInputStream() method. + * Any mail-specific encodings are decoded before this stream is returned. + * + *
    • As a Java object - using the getContent() method. + * This method returns the "content" as a Java object. + * The returned object is of course dependent on the content + * itself. In particular, a "multipart" Part's content is always a + * Multipart or subclass thereof. That is, getContent() on a + * "multipart" type Part will always return a Multipart (or subclass) object. + *
    + * + * Part provides the writeTo() method that streams + * out its bytestream in mail-safe form suitable for transmission. + * This bytestream is typically an aggregation of the Part attributes + * and its content's bytestream.

    + * + * Message and BodyPart implement the Part interface. Note that in + * MIME parlance, Part models an Entity (RFC 2045, Section 2.4). + * + * @author John Mani + */ + +public interface Part { + + /** + * Return the size of the content of this part in bytes. + * Return -1 if the size cannot be determined.

    + * + * Note that the size may not be an exact measure of the content + * size and may or may not account for any transfer encoding + * of the content. The size is appropriate for display in a + * user interface to give the user a rough idea of the size + * of this part. + * + * @return size of content in bytes + * @exception MessagingException for failures + */ + public int getSize() throws MessagingException; + + /** + * Return the number of lines in the content of this part. + * Return -1 if the number cannot be determined. + * + * Note that this number may not be an exact measure of the + * content length and may or may not account for any transfer + * encoding of the content. + * + * @return number of lines in the content. + * @exception MessagingException for failures + */ + public int getLineCount() throws MessagingException; + + /** + * Returns the Content-Type of the content of this part. + * Returns null if the Content-Type could not be determined.

    + * + * The MIME typing system is used to name Content-types. + * + * @return The ContentType of this part + * @exception MessagingException for failures + * @see javax.activation.DataHandler + */ + public String getContentType() throws MessagingException; + + /** + * Is this Part of the specified MIME type? This method + * compares only the primaryType and + * subType. + * The parameters of the content types are ignored.

    + * + * For example, this method will return true when + * comparing a Part of content type "text/plain" + * with "text/plain; charset=foobar".

    + * + * If the subType of mimeType is the + * special character '*', then the subtype is ignored during the + * comparison. + * + * @param mimeType the MIME type to test + * @return true if this part is of the specified type + * @exception MessagingException for failures + */ + public boolean isMimeType(String mimeType) throws MessagingException; + + /** + * This part should be presented as an attachment. + * @see #getDisposition + * @see #setDisposition + */ + public static final String ATTACHMENT = "attachment"; + + /** + * This part should be presented inline. + * @see #getDisposition + * @see #setDisposition + */ + public static final String INLINE = "inline"; + + /** + * Return the disposition of this part. The disposition + * describes how the part should be presented to the user. + * (See RFC 2183.) The return value should be considered + * without regard to case. For example: + *

    +     * String disp = part.getDisposition();
    +     * if (disp == null || disp.equalsIgnoreCase(Part.ATTACHMENT))
    +     *	// treat as attachment if not first part
    +     * 
    + * + * @return disposition of this part, or null if unknown + * @exception MessagingException for failures + * @see #ATTACHMENT + * @see #INLINE + * @see #getFileName + */ + public String getDisposition() throws MessagingException; + + /** + * Set the disposition of this part. + * + * @param disposition disposition of this part + * @exception IllegalWriteException if the underlying implementation + * does not support modification of this header + * @exception IllegalStateException if this Part is obtained + * from a READ_ONLY folder + * @exception MessagingException for other failures + * @see #ATTACHMENT + * @see #INLINE + * @see #setFileName + */ + public void setDisposition(String disposition) throws MessagingException; + + /** + * Return a description String for this part. This typically + * associates some descriptive information with this part. + * Returns null if none is available. + * + * @return description of this part + * @exception MessagingException for failures + */ + public String getDescription() throws MessagingException; + + /** + * Set a description String for this part. This typically + * associates some descriptive information with this part. + * + * @param description description of this part + * @exception IllegalWriteException if the underlying implementation + * does not support modification of this header + * @exception IllegalStateException if this Part is obtained + * from a READ_ONLY folder + * @exception MessagingException for other failures + */ + public void setDescription(String description) throws MessagingException; + + /** + * Get the filename associated with this part, if possible. + * Useful if this part represents an "attachment" that was + * loaded from a file. The filename will usually be a simple + * name, not including directory components. + * + * @return Filename to associate with this part + * @exception MessagingException for failures + */ + public String getFileName() throws MessagingException; + + /** + * Set the filename associated with this part, if possible. + * Useful if this part represents an "attachment" that was + * loaded from a file. The filename will usually be a simple + * name, not including directory components. + * + * @param filename Filename to associate with this part + * @exception IllegalWriteException if the underlying implementation + * does not support modification of this header + * @exception IllegalStateException if this Part is obtained + * from a READ_ONLY folder + * @exception MessagingException for other failures + */ + public void setFileName(String filename) throws MessagingException; + + /** + * Return an input stream for this part's "content". Any + * mail-specific transfer encodings will be decoded before the + * input stream is provided.

    + * + * This is typically a convenience method that just invokes + * the DataHandler's getInputStream() method. + * + * @return an InputStream + * @exception IOException this is typically thrown by the + * DataHandler. Refer to the documentation for + * javax.activation.DataHandler for more details. + * @exception MessagingException for other failures + * @see #getDataHandler + * @see javax.activation.DataHandler#getInputStream + */ + public InputStream getInputStream() + throws IOException, MessagingException; + + /** + * Return a DataHandler for the content within this part. The + * DataHandler allows clients to operate on as well as retrieve + * the content. + * + * @return DataHandler for the content + * @exception MessagingException for failures + */ + public DataHandler getDataHandler() throws MessagingException; + + /** + * Return the content as a Java object. The type of the returned + * object is of course dependent on the content itself. For example, + * the object returned for "text/plain" content is usually a String + * object. The object returned for a "multipart" content is always a + * Multipart subclass. For content-types that are unknown to the + * DataHandler system, an input stream is returned as the content

    + * + * This is a convenience method that just invokes the DataHandler's + * getContent() method + * + * @return Object + * @exception IOException this is typically thrown by the + * DataHandler. Refer to the documentation for + * javax.activation.DataHandler for more details. + * @exception MessagingException for other failures + * + * @see javax.activation.DataHandler#getContent + */ + public Object getContent() throws IOException, MessagingException; + + /** + * This method provides the mechanism to set this part's content. + * The DataHandler wraps around the actual content. + * + * @param dh The DataHandler for the content. + * @exception IllegalWriteException if the underlying implementation + * does not support modification of existing values + * @exception IllegalStateException if this Part is obtained + * from a READ_ONLY folder + * @exception MessagingException for other failures + */ + public void setDataHandler(DataHandler dh) throws MessagingException; + + /** + * A convenience method for setting this part's content. The part + * internally wraps the content in a DataHandler.

    + * + * Note that a DataContentHandler class for the specified type should + * be available to the JavaMail implementation for this to work right. + * i.e., to do setContent(foobar, "application/x-foobar"), + * a DataContentHandler for "application/x-foobar" should be installed. + * Refer to the Java Activation Framework for more information. + * + * @param obj A java object. + * @param type MIME type of this object. + * @exception IllegalWriteException if the underlying implementation + * does not support modification of existing values + * @exception IllegalStateException if this Part is obtained + * from a READ_ONLY folder + * @exception MessagingException for other failures + */ + public void setContent(Object obj, String type) + throws MessagingException; + + /** + * A convenience method that sets the given String as this + * part's content with a MIME type of "text/plain". + * + * @param text The text that is the Message's content. + * @exception IllegalWriteException if the underlying + * implementation does not support modification of + * existing values + * @exception IllegalStateException if this Part is obtained + * from a READ_ONLY folder + * @exception MessagingException for other failures + */ + public void setText(String text) throws MessagingException; + + /** + * This method sets the given Multipart object as this message's + * content. + * + * @param mp The multipart object that is the Message's content + * @exception IllegalWriteException if the underlying + * implementation does not support modification of + * existing values + * @exception IllegalStateException if this Part is obtained + * from a READ_ONLY folder + * @exception MessagingException for other failures + */ + public void setContent(Multipart mp) throws MessagingException; + + /** + * Output a bytestream for this Part. This bytestream is + * typically an aggregration of the Part attributes and + * an appropriately encoded bytestream from its 'content'.

    + * + * Classes that implement the Part interface decide on + * the appropriate encoding algorithm to be used.

    + * + * The bytestream is typically used for sending. + * + * @param os the stream to write to + * @exception IOException if an error occurs writing to the + * stream or if an error is generated + * by the javax.activation layer. + * @exception MessagingException if an error occurs fetching the + * data to be written + * + * @see javax.activation.DataHandler#writeTo + */ + public void writeTo(OutputStream os) throws IOException, MessagingException; + + /** + * Get all the headers for this header name. Returns null + * if no headers for this header name are available. + * + * @param header_name the name of this header + * @return the value fields for all headers with + * this name + * @exception MessagingException for failures + */ + public String[] getHeader(String header_name) + throws MessagingException; + + /** + * Set the value for this header_name. Replaces all existing + * header values with this new value. + * + * @param header_name the name of this header + * @param header_value the value for this header + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this Part is + * obtained from a READ_ONLY folder + * @exception MessagingException for other failures + */ + public void setHeader(String header_name, String header_value) + throws MessagingException; + /** + * Add this value to the existing values for this header_name. + * + * @param header_name the name of this header + * @param header_value the value for this header + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this Part is + * obtained from a READ_ONLY folder + * @exception MessagingException for other failures + */ + public void addHeader(String header_name, String header_value) + throws MessagingException; + /** + * Remove all headers with this name. + * + * @param header_name the name of this header + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this Part is + * obtained from a READ_ONLY folder + * @exception MessagingException for other failures + */ + public void removeHeader(String header_name) + throws MessagingException; + + /** + * Return all the headers from this part as an Enumeration of + * Header objects. + * + * @return enumeration of Header objects + * @exception MessagingException for failures + */ + public Enumeration

    getAllHeaders() throws MessagingException; + + /** + * Return matching headers from this part as an Enumeration of + * Header objects. + * + * @param header_names the headers to match + * @return enumeration of Header objects + * @exception MessagingException for failures + */ + public Enumeration
    getMatchingHeaders(String[] header_names) + throws MessagingException; + + /** + * Return non-matching headers from this envelope as an Enumeration + * of Header objects. + * + * @param header_names the headers to not match + * @return enumeration of Header objects + * @exception MessagingException for failures + */ + public Enumeration
    getNonMatchingHeaders(String[] header_names) + throws MessagingException; +} diff --git a/fine-third-default/fine-mail/src/javax/mail/PasswordAuthentication.java b/fine-third-default/fine-mail/src/javax/mail/PasswordAuthentication.java new file mode 100644 index 000000000..0b1d6314d --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/PasswordAuthentication.java @@ -0,0 +1,83 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + + +/** + * The class PasswordAuthentication is a data holder that is used by + * Authenticator. It is simply a repository for a user name and a password. + * + * @see java.net.PasswordAuthentication + * @see javax.mail.Authenticator + * @see javax.mail.Authenticator#getPasswordAuthentication() + * + * @author Bill Foote + */ + +public final class PasswordAuthentication { + + private final String userName; + private final String password; + + /** + * Initialize a new PasswordAuthentication + * @param userName the user name + * @param password The user's password + */ + public PasswordAuthentication(String userName, String password) { + this.userName = userName; + this.password = password; + } + + /** + * @return the user name + */ + public String getUserName() { + return userName; + } + + /** + * @return the password + */ + public String getPassword() { + return password; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Provider.java b/fine-third-default/fine-mail/src/javax/mail/Provider.java new file mode 100644 index 000000000..788a59992 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Provider.java @@ -0,0 +1,162 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * The Provider is a class that describes a protocol + * implementation. The values typically come from the + * javamail.providers and javamail.default.providers + * resource files. An application may also create and + * register a Provider object to dynamically add support + * for a new provider. + * + * @author Max Spivak + * @author Bill Shannon + */ +public class Provider { + + /** + * This inner class defines the Provider type. + * Currently, STORE and TRANSPORT are the only two provider types + * supported. + */ + + public static class Type { + public static final Type STORE = new Type("STORE"); + public static final Type TRANSPORT = new Type("TRANSPORT"); + + private String type; + + private Type(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } + } + + private Type type; + private String protocol, className, vendor, version; + + /** + * Create a new provider of the specified type for the specified + * protocol. The specified class implements the provider. + * + * @param type Type.STORE or Type.TRANSPORT + * @param protocol valid protocol for the type + * @param classname class name that implements this protocol + * @param vendor optional string identifying the vendor (may be null) + * @param version optional implementation version string (may be null) + * @since JavaMail 1.4 + */ + public Provider(Type type, String protocol, String classname, + String vendor, String version) { + this.type = type; + this.protocol = protocol; + this.className = classname; + this.vendor = vendor; + this.version = version; + } + + /** + * Returns the type of this Provider. + * + * @return the provider type + */ + public Type getType() { + return type; + } + + /** + * Returns the protocol supported by this Provider. + * + * @return the protocol + */ + public String getProtocol() { + return protocol; + } + + /** + * Returns the name of the class that implements the protocol. + * + * @return the class name + */ + public String getClassName() { + return className; + } + + /** + * Returns the name of the vendor associated with this implementation + * or null. + * + * @return the vendor + */ + public String getVendor() { + return vendor; + } + + /** + * Returns the version of this implementation or null if no version. + * + * @return the version + */ + public String getVersion() { + return version; + } + + /** Overrides Object.toString() */ + @Override + public String toString() { + String s = "javax.mail.Provider[" + type + "," + + protocol + "," + className; + + if (vendor != null) + s += "," + vendor; + + if (version != null) + s += "," + version; + + s += "]"; + return s; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Quota.java b/fine-third-default/fine-mail/src/javax/mail/Quota.java new file mode 100644 index 000000000..83516aa71 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Quota.java @@ -0,0 +1,127 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * This class represents a set of quotas for a given quota root. + * Each quota root has a set of resources, represented by the + * Quota.Resource class. Each resource has a name + * (for example, "STORAGE"), a current usage, and a usage limit. + * See RFC 2087. + * + * @since JavaMail 1.4 + * @author Bill Shannon + */ + +public class Quota { + + /** + * An individual resource in a quota root. + * + * @since JavaMail 1.4 + */ + public static class Resource { + /** The name of the resource. */ + public String name; + /** The current usage of the resource. */ + public long usage; + /** The usage limit for the resource. */ + public long limit; + + /** + * Construct a Resource object with the given name, + * usage, and limit. + * + * @param name the resource name + * @param usage the current usage of the resource + * @param limit the usage limit for the resource + */ + public Resource(String name, long usage, long limit) { + this.name = name; + this.usage = usage; + this.limit = limit; + } + } + + /** + * The name of the quota root. + */ + public String quotaRoot; + + /** + * The set of resources associated with this quota root. + */ + public Quota.Resource[] resources; + + /** + * Create a Quota object for the named quotaroot with no associated + * resources. + * + * @param quotaRoot the name of the quota root + */ + public Quota(String quotaRoot) { + this.quotaRoot = quotaRoot; + } + + /** + * Set a resource limit for this quota root. + * + * @param name the name of the resource + * @param limit the resource limit + */ + public void setResourceLimit(String name, long limit) { + if (resources == null) { + resources = new Quota.Resource[1]; + resources[0] = new Quota.Resource(name, 0, limit); + return; + } + for (int i = 0; i < resources.length; i++) { + if (resources[i].name.equalsIgnoreCase(name)) { + resources[i].limit = limit; + return; + } + } + Quota.Resource[] ra = new Quota.Resource[resources.length + 1]; + System.arraycopy(resources, 0, ra, 0, resources.length); + ra[ra.length - 1] = new Quota.Resource(name, 0, limit); + resources = ra; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/QuotaAwareStore.java b/fine-third-default/fine-mail/src/javax/mail/QuotaAwareStore.java new file mode 100644 index 000000000..a5c196fea --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/QuotaAwareStore.java @@ -0,0 +1,81 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * An interface implemented by Stores that support quotas. + * The {@link #getQuota getQuota} and {@link #setQuota setQuota} methods + * support the quota model defined by the IMAP QUOTA extension. + * Refer to RFC 2087 + * for more information.

    + * + * @since JavaMail 1.4 + */ +public interface QuotaAwareStore { + /** + * Get the quotas for the named folder. + * Quotas are controlled on the basis of a quota root, not + * (necessarily) a folder. The relationship between folders + * and quota roots depends on the server. Some servers + * might implement a single quota root for all folders owned by + * a user. Other servers might implement a separate quota root + * for each folder. A single folder can even have multiple + * quota roots, perhaps controlling quotas for different + * resources. + * + * @param folder the name of the folder + * @return array of Quota objects + * @exception MessagingException if the server doesn't support the + * QUOTA extension + */ + Quota[] getQuota(String folder) throws MessagingException; + + /** + * Set the quotas for the quota root specified in the quota argument. + * Typically this will be one of the quota roots obtained from the + * getQuota method, but it need not be. + * + * @param quota the quota to set + * @exception MessagingException if the server doesn't support the + * QUOTA extension + */ + void setQuota(Quota quota) throws MessagingException; +} diff --git a/fine-third-default/fine-mail/src/javax/mail/ReadOnlyFolderException.java b/fine-third-default/fine-mail/src/javax/mail/ReadOnlyFolderException.java new file mode 100644 index 000000000..fe7b1ab11 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/ReadOnlyFolderException.java @@ -0,0 +1,106 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * This exception is thrown when an attempt is made to open a folder + * read-write access when the folder is marked read-only.

    + * + * The getMessage() method returns more detailed information about the + * error that caused this exception.

    + * + * @author Jim Glennon + */ + +public class ReadOnlyFolderException extends MessagingException { + transient private Folder folder; + + private static final long serialVersionUID = 5711829372799039325L; + + /** + * Constructs a ReadOnlyFolderException with the specified + * folder and no detail message. + * + * @param folder the Folder + * @since JavaMail 1.2 + */ + public ReadOnlyFolderException(Folder folder) { + this(folder, null); + } + + /** + * Constructs a ReadOnlyFolderException with the specified + * detail message. + * + * @param folder The Folder + * @param message The detailed error message + * @since JavaMail 1.2 + */ + public ReadOnlyFolderException(Folder folder, String message) { + super(message); + this.folder = folder; + } + + /** + * Constructs a ReadOnlyFolderException with the specified + * detail message and embedded exception. The exception is chained + * to this exception. + * + * @param folder The Folder + * @param message The detailed error message + * @param e The embedded exception + * @since JavaMail 1.5 + */ + public ReadOnlyFolderException(Folder folder, String message, Exception e) { + super(message, e); + this.folder = folder; + } + + /** + * Returns the Folder object. + * + * @return the Folder + * @since JavaMail 1.2 + */ + public Folder getFolder() { + return folder; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/SendFailedException.java b/fine-third-default/fine-mail/src/javax/mail/SendFailedException.java new file mode 100644 index 000000000..8e5d66ae1 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/SendFailedException.java @@ -0,0 +1,140 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * This exception is thrown when the message cannot be sent.

    + * + * The exception includes those addresses to which the message could not be + * sent as well as the valid addresses to which the message was sent and + * valid addresses to which the message was not sent. + * + * @see javax.mail.Transport#send + * @see javax.mail.Transport#sendMessage + * @see javax.mail.event.TransportEvent + * + * @author John Mani + * @author Max Spivak + */ + +public class SendFailedException extends MessagingException { + transient protected Address[] invalid; + transient protected Address[] validSent; + transient protected Address[] validUnsent; + + private static final long serialVersionUID = -6457531621682372913L; + + /** + * Constructs a SendFailedException with no detail message. + */ + public SendFailedException() { + super(); + } + + /** + * Constructs a SendFailedException with the specified detail message. + * @param s the detail message + */ + public SendFailedException(String s) { + super(s); + } + + /** + * Constructs a SendFailedException with the specified + * Exception and detail message. The specified exception is chained + * to this exception. + * @param s the detail message + * @param e the embedded exception + * @see #getNextException + * @see #setNextException + */ + public SendFailedException(String s, Exception e) { + super(s, e); + } + + + /** + * Constructs a SendFailedException with the specified string + * and the specified address objects. + * + * @param msg the detail message + * @param ex the embedded exception + * @param validSent valid addresses to which message was sent + * @param validUnsent valid addresses to which message was not sent + * @param invalid the invalid addresses + * @see #getNextException + * @see #setNextException + */ + public SendFailedException(String msg, Exception ex, Address[] validSent, + Address[] validUnsent, Address[] invalid) { + super(msg, ex); + this.validSent = validSent; + this.validUnsent = validUnsent; + this.invalid = invalid; + } + + /** + * Return the addresses to which this message was sent succesfully. + * @return Addresses to which the message was sent successfully or null + */ + public Address[] getValidSentAddresses() { + return validSent; + } + + /** + * Return the addresses that are valid but to which this message + * was not sent. + * @return Addresses that are valid but to which the message was + * not sent successfully or null + */ + public Address[] getValidUnsentAddresses() { + return validUnsent; + } + + /** + * Return the addresses to which this message could not be sent. + * + * @return Addresses to which the message sending failed or null; + */ + public Address[] getInvalidAddresses() { + return invalid; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Service.java b/fine-third-default/fine-mail/src/javax/mail/Service.java new file mode 100644 index 000000000..b2815e75e --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Service.java @@ -0,0 +1,681 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.Executor; +import javax.mail.event.*; + +/** + * An abstract class that contains the functionality + * common to messaging services, such as stores and transports.

    + * A messaging service is created from a Session and is + * named using a URLName. A service must be connected + * before it can be used. Connection events are sent to reflect + * its connection status. + * + * @author Christopher Cotton + * @author Bill Shannon + * @author Kanwar Oberoi + */ + +public abstract class Service implements AutoCloseable { + + /** + * The session from which this service was created. + */ + protected Session session; + + /** + * The URLName of this service. + */ + protected volatile URLName url = null; + + /** + * Debug flag for this service. Set from the session's debug + * flag when this service is created. + */ + protected boolean debug = false; + + private boolean connected = false; + + /* + * connectionListeners is a Vector, initialized here, + * because we depend on it always existing and depend + * on the synchronization that Vector provides. + * (Sychronizing on the Service object itself can cause + * deadlocks when notifying listeners.) + */ + private final Vector connectionListeners + = new Vector<>(); + + /** + * The queue of events to be delivered. + */ + private final EventQueue q; + + /** + * Constructor. + * + * @param session Session object for this service + * @param urlname URLName object to be used for this service + */ + protected Service(Session session, URLName urlname) { + this.session = session; + debug = session.getDebug(); + url = urlname; + + /* + * Initialize the URLName with default values. + * The URLName will be updated when connect is called. + */ + String protocol = null; + String host = null; + int port = -1; + String user = null; + String password = null; + String file = null; + + // get whatever information we can from the URL + // XXX - url should always be non-null here, Session + // passes it into the constructor + if (url != null) { + protocol = url.getProtocol(); + host = url.getHost(); + port = url.getPort(); + user = url.getUsername(); + password = url.getPassword(); + file = url.getFile(); + } + + // try to get protocol-specific default properties + if (protocol != null) { + if (host == null) + host = session.getProperty("mail." + protocol + ".host"); + if (user == null) + user = session.getProperty("mail." + protocol + ".user"); + } + + // try to get mail-wide default properties + if (host == null) + host = session.getProperty("mail.host"); + + if (user == null) + user = session.getProperty("mail.user"); + + // try using the system username + if (user == null) { + try { + user = System.getProperty("user.name"); + } catch (SecurityException sex) { + // XXX - it's not worth creating a MailLogger just for this + //logger.log(Level.CONFIG, "Can't get user.name property", sex); + } + } + + url = new URLName(protocol, host, port, file, user, password); + + // create or choose the appropriate event queue + String scope = + session.getProperties().getProperty("mail.event.scope", "folder"); + Executor executor = + (Executor)session.getProperties().get("mail.event.executor"); + if (scope.equalsIgnoreCase("application")) + q = EventQueue.getApplicationEventQueue(executor); + else if (scope.equalsIgnoreCase("session")) + q = session.getEventQueue(); + else // if (scope.equalsIgnoreCase("store") || + // scope.equalsIgnoreCase("folder")) + q = new EventQueue(executor); + } + + /** + * A generic connect method that takes no parameters. Subclasses + * can implement the appropriate authentication schemes. Subclasses + * that need additional information might want to use some properties + * or might get it interactively using a popup window.

    + * + * If the connection is successful, an "open" ConnectionEvent + * is delivered to any ConnectionListeners on this service.

    + * + * Most clients should just call this method to connect to the service.

    + * + * It is an error to connect to an already connected service.

    + * + * The implementation provided here simply calls the following + * connect(String, String, String) method with nulls. + * + * @exception AuthenticationFailedException for authentication failures + * @exception MessagingException for other failures + * @exception IllegalStateException if the service is already connected + * + * @see javax.mail.event.ConnectionEvent + */ + public void connect() throws MessagingException { + connect(null, null, null); + } + + /** + * Connect to the specified address. This method provides a simple + * authentication scheme that requires a username and password.

    + * + * If the connection is successful, an "open" ConnectionEvent + * is delivered to any ConnectionListeners on this service.

    + * + * It is an error to connect to an already connected service.

    + * + * The implementation in the Service class will collect defaults + * for the host, user, and password from the session, from the + * URLName for this service, and from the supplied + * parameters and then call the protocolConnect method. + * If the protocolConnect method returns false, + * the user will be prompted for any missing information and the + * protocolConnect method will be called again. The + * subclass should override the protocolConnect method. + * The subclass should also implement the getURLName + * method, or use the implementation in this class.

    + * + * On a successful connection, the setURLName method is + * called with a URLName that includes the information used to make + * the connection, including the password.

    + * + * If the username passed in is null, a default value will be chosen + * as described above. + * + * If the password passed in is null and this is the first successful + * connection to this service, the user name and the password + * collected from the user will be saved as defaults for subsequent + * connection attempts to this same service when using other Service object + * instances (the connection information is typically always saved within + * a particular Service object instance). The password is saved using the + * Session method setPasswordAuthentication. If the + * password passed in is not null, it is not saved, on the assumption + * that the application is managing passwords explicitly. + * + * @param host the host to connect to + * @param user the user name + * @param password this user's password + * @exception AuthenticationFailedException for authentication failures + * @exception MessagingException for other failures + * @exception IllegalStateException if the service is already connected + * @see javax.mail.event.ConnectionEvent + * @see javax.mail.Session#setPasswordAuthentication + */ + public void connect(String host, String user, String password) + throws MessagingException { + connect(host, -1, user, password); + } + + /** + * Connect to the current host using the specified username + * and password. This method is equivalent to calling the + * connect(host, user, password) method with null + * for the host name. + * + * @param user the user name + * @param password this user's password + * @exception AuthenticationFailedException for authentication failures + * @exception MessagingException for other failures + * @exception IllegalStateException if the service is already connected + * @see javax.mail.event.ConnectionEvent + * @see javax.mail.Session#setPasswordAuthentication + * @see #connect(java.lang.String, java.lang.String, java.lang.String) + * @since JavaMail 1.4 + */ + public void connect(String user, String password) + throws MessagingException { + connect(null, user, password); + } + + /** + * Similar to connect(host, user, password) except a specific port + * can be specified. + * + * @param host the host to connect to + * @param port the port to connect to (-1 means the default port) + * @param user the user name + * @param password this user's password + * @exception AuthenticationFailedException for authentication failures + * @exception MessagingException for other failures + * @exception IllegalStateException if the service is already connected + * @see #connect(java.lang.String, java.lang.String, java.lang.String) + * @see javax.mail.event.ConnectionEvent + */ + public synchronized void connect(String host, int port, + String user, String password) throws MessagingException { + + // see if the service is already connected + if (isConnected()) + throw new IllegalStateException("already connected"); + + PasswordAuthentication pw; + boolean connected = false; + boolean save = false; + String protocol = null; + String file = null; + + // get whatever information we can from the URL + // XXX - url should always be non-null here, Session + // passes it into the constructor + if (url != null) { + protocol = url.getProtocol(); + if (host == null) + host = url.getHost(); + if (port == -1) + port = url.getPort(); + + if (user == null) { + user = url.getUsername(); + if (password == null) // get password too if we need it + password = url.getPassword(); + } else { + if (password == null && user.equals(url.getUsername())) + // only get the password if it matches the username + password = url.getPassword(); + } + + file = url.getFile(); + } + + // try to get protocol-specific default properties + if (protocol != null) { + if (host == null) + host = session.getProperty("mail." + protocol + ".host"); + if (user == null) + user = session.getProperty("mail." + protocol + ".user"); + } + + // try to get mail-wide default properties + if (host == null) + host = session.getProperty("mail.host"); + + if (user == null) + user = session.getProperty("mail.user"); + + // try using the system username + if (user == null) { + try { + user = System.getProperty("user.name"); + } catch (SecurityException sex) { + // XXX - it's not worth creating a MailLogger just for this + //logger.log(Level.CONFIG, "Can't get user.name property", sex); + } + } + + // if we don't have a password, look for saved authentication info + if (password == null && url != null) { + // canonicalize the URLName + setURLName(new URLName(protocol, host, port, file, user, null)); + pw = session.getPasswordAuthentication(getURLName()); + if (pw != null) { + if (user == null) { + user = pw.getUserName(); + password = pw.getPassword(); + } else if (user.equals(pw.getUserName())) { + password = pw.getPassword(); + } + } else + save = true; + } + + // try connecting, if the protocol needs some missing + // information (user, password) it will not connect. + // if it tries to connect and fails, remember why for later. + AuthenticationFailedException authEx = null; + try { + connected = protocolConnect(host, port, user, password); + } catch (AuthenticationFailedException ex) { + authEx = ex; + } + + // if not connected, ask the user and try again + if (!connected) { + InetAddress addr; + try { + addr = InetAddress.getByName(host); + } catch (UnknownHostException e) { + addr = null; + } + pw = session.requestPasswordAuthentication( + addr, port, + protocol, + null, user); + if (pw != null) { + user = pw.getUserName(); + password = pw.getPassword(); + + // have the service connect again + connected = protocolConnect(host, port, user, password); + } + } + + // if we're not connected by now, we give up + if (!connected) { + if (authEx != null) + throw authEx; + else if (user == null) + throw new AuthenticationFailedException( + "failed to connect, no user name specified?"); + else if (password == null) + throw new AuthenticationFailedException( + "failed to connect, no password specified?"); + else + throw new AuthenticationFailedException("failed to connect"); + } + + setURLName(new URLName(protocol, host, port, file, user, password)); + + if (save) + session.setPasswordAuthentication(getURLName(), + new PasswordAuthentication(user, password)); + + // set our connected state + setConnected(true); + + // finally, deliver the connection event + notifyConnectionListeners(ConnectionEvent.OPENED); + } + + + /** + * The service implementation should override this method to + * perform the actual protocol-specific connection attempt. + * The default implementation of the connect method + * calls this method as needed.

    + * + * The protocolConnect method should return + * false if a user name or password is required + * for authentication but the corresponding parameter is null; + * the connect method will prompt the user when + * needed to supply missing information. This method may + * also return false if authentication fails for + * the supplied user name or password. Alternatively, this method + * may throw an AuthenticationFailedException when authentication + * fails. This exception may include a String message with more + * detail about the failure.

    + * + * The protocolConnect method should throw an + * exception to report failures not related to authentication, + * such as an invalid host name or port number, loss of a + * connection during the authentication process, unavailability + * of the server, etc. + * + * @param host the name of the host to connect to + * @param port the port to use (-1 means use default port) + * @param user the name of the user to login as + * @param password the user's password + * @return true if connection successful, false if authentication failed + * @exception AuthenticationFailedException for authentication failures + * @exception MessagingException for non-authentication failures + */ + protected boolean protocolConnect(String host, int port, String user, + String password) throws MessagingException { + return false; + } + + /** + * Is this service currently connected?

    + * + * This implementation uses a private boolean field to + * store the connection state. This method returns the value + * of that field.

    + * + * Subclasses may want to override this method to verify that any + * connection to the message store is still alive. + * + * @return true if the service is connected, false if it is not connected + */ + public synchronized boolean isConnected() { + return connected; + } + + /** + * Set the connection state of this service. The connection state + * will automatically be set by the service implementation during the + * connect and close methods. + * Subclasses will need to call this method to set the state + * if the service was automatically disconnected.

    + * + * The implementation in this class merely sets the private field + * returned by the isConnected method. + * + * @param connected true if the service is connected, + * false if it is not connected + */ + protected synchronized void setConnected(boolean connected) { + this.connected = connected; + } + + /** + * Close this service and terminate its connection. A close + * ConnectionEvent is delivered to any ConnectionListeners. Any + * Messaging components (Folders, Messages, etc.) belonging to this + * service are invalid after this service is closed. Note that the service + * is closed even if this method terminates abnormally by throwing + * a MessagingException.

    + * + * This implementation uses setConnected(false) to set + * this service's connected state to false. It will then + * send a close ConnectionEvent to any registered ConnectionListeners. + * Subclasses overriding this method to do implementation specific + * cleanup should call this method as a last step to insure event + * notification, probably by including a call to super.close() + * in a finally clause. + * + * @see javax.mail.event.ConnectionEvent + * @throws MessagingException for errors while closing + */ + public synchronized void close() throws MessagingException { + setConnected(false); + notifyConnectionListeners(ConnectionEvent.CLOSED); + } + + /** + * Return a URLName representing this service. The returned URLName + * does not include the password field.

    + * + * Subclasses should only override this method if their + * URLName does not follow the standard format.

    + * + * The implementation in the Service class returns (usually a copy of) + * the url field with the password and file information + * stripped out. + * + * @return the URLName representing this service + * @see URLName + */ + public URLName getURLName() { + URLName url = this.url; // snapshot + if (url != null && (url.getPassword() != null || url.getFile() != null)) + return new URLName(url.getProtocol(), url.getHost(), + url.getPort(), null /* no file */, + url.getUsername(), null /* no password */); + else + return url; + } + + /** + * Set the URLName representing this service. + * Normally used to update the url field + * after a service has successfully connected.

    + * + * Subclasses should only override this method if their + * URL does not follow the standard format. In particular, + * subclasses should override this method if their URL + * does not require all the possible fields supported by + * URLName; a new URLName should + * be constructed with any unneeded fields removed.

    + * + * The implementation in the Service class simply sets the + * url field. + * + * @param url the URLName + * @see URLName + */ + protected void setURLName(URLName url) { + this.url = url; + } + + /** + * Add a listener for Connection events on this service.

    + * + * The default implementation provided here adds this listener + * to an internal list of ConnectionListeners. + * + * @param l the Listener for Connection events + * @see javax.mail.event.ConnectionEvent + */ + public void addConnectionListener(ConnectionListener l) { + connectionListeners.addElement(l); + } + + /** + * Remove a Connection event listener.

    + * + * The default implementation provided here removes this listener + * from the internal list of ConnectionListeners. + * + * @param l the listener + * @see #addConnectionListener + */ + public void removeConnectionListener(ConnectionListener l) { + connectionListeners.removeElement(l); + } + + /** + * Notify all ConnectionListeners. Service implementations are + * expected to use this method to broadcast connection events.

    + * + * The provided default implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the registered + * ConnectionListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param type the ConnectionEvent type + */ + protected void notifyConnectionListeners(int type) { + /* + * Don't bother queuing an event if there's no listeners. + * Yes, listeners could be removed after checking, which + * just makes this an expensive no-op. + */ + if (connectionListeners.size() > 0) { + ConnectionEvent e = new ConnectionEvent(this, type); + queueEvent(e, connectionListeners); + } + + /* Fix for broken JDK1.1.x Garbage collector : + * The 'conservative' GC in JDK1.1.x occasionally fails to + * garbage-collect Threads which are in the wait state. + * This would result in thread (and consequently memory) leaks. + * + * We attempt to fix this by sending a 'terminator' event + * to the queue, after we've sent the CLOSED event. The + * terminator event causes the event-dispatching thread to + * self destruct. + */ + if (type == ConnectionEvent.CLOSED) + q.terminateQueue(); + } + + /** + * Return getURLName.toString() if this service has a URLName, + * otherwise it will return the default toString. + */ + @Override + public String toString() { + URLName url = getURLName(); + if (url != null) + return url.toString(); + else + return super.toString(); + } + + /** + * Add the event and vector of listeners to the queue to be delivered. + * + * @param event the event + * @param vector the vector of listeners + */ + protected void queueEvent(MailEvent event, + Vector vector) { + /* + * Copy the vector in order to freeze the state of the set + * of EventListeners the event should be delivered to prior + * to delivery. This ensures that any changes made to the + * Vector from a target listener's method during the delivery + * of this event will not take effect until after the event is + * delivered. + */ + @SuppressWarnings("unchecked") + Vector v = (Vector)vector.clone(); + q.enqueue(event, v); + } + + /** + * Stop the event dispatcher thread so the queue can be garbage collected. + */ + @Override + protected void finalize() throws Throwable { + try { + q.terminateQueue(); + } finally { + super.finalize(); + } + } + + /** + * Package private method to allow Folder to get the Session for a Store. + */ + Session getSession() { + return session; + } + + /** + * Package private method to allow Folder to get the EventQueue for a Store. + */ + EventQueue getEventQueue() { + return q; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Session.java b/fine-third-default/fine-mail/src/javax/mail/Session.java new file mode 100644 index 000000000..16e757ef0 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Session.java @@ -0,0 +1,1445 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.lang.reflect.*; +import java.io.*; +import java.net.*; +import java.security.*; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.ServiceLoader; +import java.util.logging.Level; +import java.util.concurrent.Executor; + +import com.sun.mail.util.LineInputStream; +import com.sun.mail.util.MailLogger; + +/** + * The Session class represents a mail session and is not subclassed. + * It collects together properties and defaults used by the mail API's. + * A single default session can be shared by multiple applications on the + * desktop. Unshared sessions can also be created.

    + * + * The Session class provides access to the protocol providers that + * implement the Store, Transport, and related + * classes. The protocol providers are configured using the following files: + *

      + *
    • javamail.providers and + * javamail.default.providers
    • + *
    • javamail.address.map and + * javamail.default.address.map
    • + *
    + *

    + * Each javamail.X resource file is searched for using + * three methods in the following order: + *

      + *
    1. java.home/conf/javamail.X
    2. + *
    3. META-INF/javamail.X
    4. + *
    5. META-INF/javamail.default.X
    6. + *
    + *

    + * (Where java.home is the value of the "java.home" System property + * and conf is the directory named "conf" if it exists, + * otherwise the directory named "lib"; the "conf" directory was + * introduced in JDK 1.9.) + *

    + * The first method allows the user to include their own version of the + * resource file by placing it in the conf directory where the + * java.home property points. The second method allows an + * application that uses the JavaMail APIs to include their own resource + * files in their application's or jar file's META-INF + * directory. The javamail.default.X default files + * are part of the JavaMail mail.jar file and should not be + * supplied by users.

    + * + * File location depends upon how the ClassLoader method + * getResource is implemented. Usually, the + * getResource method searches through CLASSPATH until it + * finds the requested file and then stops.

    + * + * The ordering of entries in the resource files matters. If multiple + * entries exist, the first entries take precedence over the later + * entries. For example, the first IMAP provider found will be set as the + * default IMAP implementation until explicitly changed by the + * application. The user- or system-supplied resource files augment, they + * do not override, the default files included with the JavaMail APIs. + * This means that all entries in all files loaded will be available.

    + * + * javamail.providers and + * javamail.default.providers

    + * + * These resource files specify the stores and transports that are + * available on the system, allowing an application to "discover" what + * store and transport implementations are available. The protocol + * implementations are listed one per line. The file format defines four + * attributes that describe a protocol implementation. Each attribute is + * an "="-separated name-value pair with the name in lowercase. Each + * name-value pair is semi-colon (";") separated. The following names + * are defined. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    + * Attribute Names in Providers Files + *
    NameDescription
    protocolName assigned to protocol. + * For example, smtp for Transport.
    typeValid entries are store and transport.
    classClass name that implements this protocol.
    vendorOptional string identifying the vendor.
    versionOptional string identifying the version.

    + * + * Here's an example of META-INF/javamail.default.providers + * file contents: + *

    + * protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Oracle;
    + * protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Oracle;
    + * 

    + * + * The current implementation also supports configuring providers using + * the Java SE {@link java.util.ServiceLoader ServiceLoader} mechanism. + * When creating your own provider, create a {@link Provider} subclass, + * for example: + *

    + * package com.example;
    + *
    + * import javax.mail.Provider;
    + *
    + * public class MyProvider extends Provider {
    + *     public MyProvider() {
    + *         super(Provider.Type.STORE, "myprot", MyStore.class.getName(),
    + *             "Example", null);
    + *     }
    + * }
    + * 
    + * Then include a file named META-INF/services/javax.mail.Provider + * in your jar file that lists the name of your Provider class: + *
    + * com.example.MyProvider
    + * 
    + *

    + * + * javamail.address.map and + * javamail.default.address.map

    + * + * These resource files map transport address types to the transport + * protocol. The getType method of + * javax.mail.Address returns the address type. The + * javamail.address.map file maps the transport type to the + * protocol. The file format is a series of name-value pairs. Each key + * name should correspond to an address type that is currently installed + * on the system; there should also be an entry for each + * javax.mail.Address implementation that is present if it is + * to be used. For example, the + * javax.mail.internet.InternetAddress method + * getType returns "rfc822". Each referenced protocol should + * be installed on the system. For the case of news, below, + * the client should install a Transport provider supporting the nntp + * protocol.

    + * + * Here are the typical contents of a javamail.address.map file: + *

    + * rfc822=smtp
    + * news=nntp
    + * 
    + * + * @author John Mani + * @author Bill Shannon + * @author Max Spivak + */ + +public final class Session { + + private final Properties props; + private final Authenticator authenticator; + private final Hashtable authTable + = new Hashtable<>(); + private boolean debug = false; + private PrintStream out; // debug output stream + private MailLogger logger; + private List providers; + private final Map providersByProtocol = new HashMap<>(); + private final Map providersByClassName = new HashMap<>(); + private final Properties addressMap = new Properties(); + // maps type to protocol + private boolean loadedProviders; // javamail.[default.]providers loaded? + // the queue of events to be delivered, if mail.event.scope===session + private final EventQueue q; + + // The default session. + private static Session defaultSession = null; + + private static final String confDir; + + static { + String dir = null; + try { + dir = AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public String run() { + String home = System.getProperty("java.home"); + String newdir = home + File.separator + "conf"; + File conf = new File(newdir); + if (conf.exists()) + return newdir + File.separator; + else + return home + File.separator + + "lib" + File.separator; + } + }); + } catch (Exception ex) { + // ignore any exceptions + } + confDir = dir; + } + + // Constructor is not public + private Session(Properties props, Authenticator authenticator) { + this.props = props; + this.authenticator = authenticator; + + if (Boolean.valueOf(props.getProperty("mail.debug")).booleanValue()) + debug = true; + + initLogger(); + logger.log(Level.CONFIG, "JavaMail version {0}", Version.version); + + // get the Class associated with the Authenticator + Class cl; + if (authenticator != null) + cl = authenticator.getClass(); + else + cl = this.getClass(); + // load the resources + loadAddressMap(cl); + q = new EventQueue((Executor)props.get("mail.event.executor")); + } + + private final synchronized void initLogger() { + logger = new MailLogger(this.getClass(), "DEBUG", debug, getDebugOut()); + } + + /** + * Get a new Session object. + * + * @param props Properties object that hold relevant properties.
    + * It is expected that the client supplies values + * for the properties listed in Appendix A of the + * JavaMail spec (particularly mail.store.protocol, + * mail.transport.protocol, mail.host, mail.user, + * and mail.from) as the defaults are unlikely to + * work in all cases. + * @param authenticator Authenticator object used to call back to + * the application when a user name and password is + * needed. + * @return a new Session object + * @see javax.mail.Authenticator + */ + public static Session getInstance(Properties props, + Authenticator authenticator) { + return new Session(props, authenticator); + } + + /** + * Get a new Session object. + * + * @param props Properties object that hold relevant properties.
    + * It is expected that the client supplies values + * for the properties listed in Appendix A of the + * JavaMail spec (particularly mail.store.protocol, + * mail.transport.protocol, mail.host, mail.user, + * and mail.from) as the defaults are unlikely to + * work in all cases. + * @return a new Session object + * @since JavaMail 1.2 + */ + public static Session getInstance(Properties props) { + return new Session(props, null); + } + + /** + * Get the default Session object. If a default has not yet been + * setup, a new Session object is created and installed as the + * default.

    + * + * Since the default session is potentially available to all + * code executing in the same Java virtual machine, and the session + * can contain security sensitive information such as user names + * and passwords, access to the default session is restricted. + * The Authenticator object, which must be created by the caller, + * is used indirectly to check access permission. The Authenticator + * object passed in when the session is created is compared with + * the Authenticator object passed in to subsequent requests to + * get the default session. If both objects are the same, or are + * from the same ClassLoader, the request is allowed. Otherwise, + * it is denied.

    + * + * Note that if the Authenticator object used to create the session + * is null, anyone can get the default session by passing in null.

    + * + * Note also that the Properties object is used only the first time + * this method is called, when a new Session object is created. + * Subsequent calls return the Session object that was created by the + * first call, and ignore the passed Properties object. Use the + * getInstance method to get a new Session object every + * time the method is called.

    + * + * Additional security Permission objects may be used to + * control access to the default session.

    + * + * In the current implementation, if a SecurityManager is set, the + * caller must have the RuntimePermission("setFactory") + * permission. + * + * @param props Properties object. Used only if a new Session + * object is created.
    + * It is expected that the client supplies values + * for the properties listed in Appendix A of the + * JavaMail spec (particularly mail.store.protocol, + * mail.transport.protocol, mail.host, mail.user, + * and mail.from) as the defaults are unlikely to + * work in all cases. + * @param authenticator Authenticator object. Used only if a + * new Session object is created. Otherwise, + * it must match the Authenticator used to create + * the Session. + * @return the default Session object + */ + public static synchronized Session getDefaultInstance(Properties props, + Authenticator authenticator) { + if (defaultSession == null) { + SecurityManager security = System.getSecurityManager(); + if (security != null) + security.checkSetFactory(); + defaultSession = new Session(props, authenticator); + } else { + // have to check whether caller is allowed to see default session + if (defaultSession.authenticator == authenticator) + ; // either same object or both null, either way OK + else if (defaultSession.authenticator != null && + authenticator != null && + defaultSession.authenticator.getClass().getClassLoader() == + authenticator.getClass().getClassLoader()) + ; // both objects came from the same class loader, OK + else + // anything else is not allowed + throw new SecurityException("Access to default session denied"); + } + + return defaultSession; + } + + /** + * Get the default Session object. If a default has not yet been + * setup, a new Session object is created and installed as the + * default.

    + * + * Note that a default session created with no Authenticator is + * available to all code executing in the same Java virtual + * machine, and the session can contain security sensitive + * information such as user names and passwords. + * + * @param props Properties object. Used only if a new Session + * object is created.
    + * It is expected that the client supplies values + * for the properties listed in Appendix A of the + * JavaMail spec (particularly mail.store.protocol, + * mail.transport.protocol, mail.host, mail.user, + * and mail.from) as the defaults are unlikely to + * work in all cases. + * @return the default Session object + * @since JavaMail 1.2 + */ + public static Session getDefaultInstance(Properties props) { + return getDefaultInstance(props, null); + } + + /** + * Set the debug setting for this Session. + *

    + * Since the debug setting can be turned on only after the Session + * has been created, to turn on debugging in the Session + * constructor, set the property mail.debug in the + * Properties object passed in to the constructor to true. The + * value of the mail.debug property is used to + * initialize the per-Session debugging flag. Subsequent calls to + * the setDebug method manipulate the per-Session + * debugging flag and have no affect on the mail.debug + * property. + * + * @param debug Debug setting + */ + public synchronized void setDebug(boolean debug) { + this.debug = debug; + initLogger(); + logger.log(Level.CONFIG, "setDebug: JavaMail version {0}", + Version.version); + } + + /** + * Get the debug setting for this Session. + * + * @return current debug setting + */ + public synchronized boolean getDebug() { + return debug; + } + + /** + * Set the stream to be used for debugging output for this session. + * If out is null, System.out will be used. + * Note that debugging output that occurs before any session is created, + * as a result of setting the mail.debug system property, + * will always be sent to System.out. + * + * @param out the PrintStream to use for debugging output + * @since JavaMail 1.3 + */ + public synchronized void setDebugOut(PrintStream out) { + this.out = out; + initLogger(); + } + + /** + * Returns the stream to be used for debugging output. If no stream + * has been set, System.out is returned. + * + * @return the PrintStream to use for debugging output + * @since JavaMail 1.3 + */ + public synchronized PrintStream getDebugOut() { + if (out == null) + return System.out; + else + return out; + } + + /** + * This method returns an array of all the implementations installed + * via the javamail.[default.]providers files that can + * be loaded using the ClassLoader available to this application. + * + * @return Array of configured providers + */ + public synchronized Provider[] getProviders() { + List plist = new ArrayList(); + boolean needFallback = true; + // first, add all the services + ServiceLoader loader = ServiceLoader.load(Provider.class); + for (Provider p : loader) { + plist.add(p); + needFallback = false; + } + // then, add all the providers from config files + if (!loadedProviders) + loadProviders(needFallback); + if (providers != null) + plist.addAll(providers); + Provider[] _providers = new Provider[plist.size()]; + plist.toArray(_providers); + return _providers; + } + + /** + * Returns the default Provider for the protocol + * specified. Checks mail.<protocol>.class property + * first and if it exists, returns the Provider + * associated with this implementation. If it doesn't exist, + * returns the Provider that appeared first in the + * configuration files. If an implementation for the protocol + * isn't found, throws NoSuchProviderException + * + * @param protocol Configured protocol (i.e. smtp, imap, etc) + * @return Currently configured Provider for the specified protocol + * @exception NoSuchProviderException If a provider for the given + * protocol is not found. + */ + public synchronized Provider getProvider(String protocol) + throws NoSuchProviderException { + + if (protocol == null || protocol.length() <= 0) { + throw new NoSuchProviderException("Invalid protocol: null"); + } + + Provider _provider = null; + + // check if the mail..class property exists + String _className = props.getProperty("mail."+protocol+".class"); + if (_className != null) { + if (logger.isLoggable(Level.FINE)) { + logger.fine("mail."+protocol+ + ".class property exists and points to " + + _className); + } + _provider = getProviderByClassName(_className); + } + + if (_provider == null) + _provider = getProviderByProtocol(protocol); + + if (_provider == null) { + throw new NoSuchProviderException("No provider for " + protocol); + } else { + if (logger.isLoggable(Level.FINE)) { + logger.fine("getProvider() returning " + _provider.toString()); + } + return _provider; + } + } + + /** + * Set the passed Provider to be the default implementation + * for the protocol in Provider.protocol overriding any previous values. + * + * @param provider Currently configured Provider which will be + * set as the default for the protocol + * @exception NoSuchProviderException If the provider passed in + * is invalid. + */ + public synchronized void setProvider(Provider provider) + throws NoSuchProviderException { + if (provider == null) { + throw new NoSuchProviderException("Can't set null provider"); + } + providersByProtocol.put(provider.getProtocol(), provider); + providersByClassName.put(provider.getClassName(), provider); + props.put("mail." + provider.getProtocol() + ".class", + provider.getClassName()); + } + + + /** + * Get a Store object that implements this user's desired Store + * protocol. The mail.store.protocol property specifies the + * desired protocol. If an appropriate Store object is not obtained, + * NoSuchProviderException is thrown + * + * @return a Store object + * @exception NoSuchProviderException If a provider for the given + * protocol is not found. + */ + public Store getStore() throws NoSuchProviderException { + return getStore(getProperty("mail.store.protocol")); + } + + /** + * Get a Store object that implements the specified protocol. If an + * appropriate Store object cannot be obtained, + * NoSuchProviderException is thrown. + * + * @param protocol the Store protocol + * @return a Store object + * @exception NoSuchProviderException If a provider for the given + * protocol is not found. + */ + public Store getStore(String protocol) throws NoSuchProviderException { + return getStore(new URLName(protocol, null, -1, null, null, null)); + } + + + /** + * Get a Store object for the given URLName. If the requested Store + * object cannot be obtained, NoSuchProviderException is thrown. + * + * The "scheme" part of the URL string (Refer RFC 1738) is used + * to locate the Store protocol.

    + * + * @param url URLName that represents the desired Store + * @return a closed Store object + * @see #getFolder(URLName) + * @see javax.mail.URLName + * @exception NoSuchProviderException If a provider for the given + * URLName is not found. + */ + public Store getStore(URLName url) throws NoSuchProviderException { + String protocol = url.getProtocol(); + Provider p = getProvider(protocol); + return getStore(p, url); + } + + /** + * Get an instance of the store specified by Provider. Instantiates + * the store and returns it. + * + * @param provider Store Provider that will be instantiated + * @return Instantiated Store + * @exception NoSuchProviderException If a provider for the given + * Provider is not found. + */ + public Store getStore(Provider provider) throws NoSuchProviderException { + return getStore(provider, null); + } + + + /** + * Get an instance of the store specified by Provider. If the URLName + * is not null, uses it, otherwise creates a new one. Instantiates + * the store and returns it. This is a private method used by + * getStore(Provider) and getStore(URLName) + * + * @param provider Store Provider that will be instantiated + * @param url URLName used to instantiate the Store + * @return Instantiated Store + * @exception NoSuchProviderException If a provider for the given + * Provider/URLName is not found. + */ + private Store getStore(Provider provider, URLName url) + throws NoSuchProviderException { + + // make sure we have the correct type of provider + if (provider == null || provider.getType() != Provider.Type.STORE ) { + throw new NoSuchProviderException("invalid provider"); + } + + return getService(provider, url, Store.class); + } + + /** + * Get a closed Folder object for the given URLName. If the requested + * Folder object cannot be obtained, null is returned.

    + * + * The "scheme" part of the URL string (Refer RFC 1738) is used + * to locate the Store protocol. The rest of the URL string (that is, + * the "schemepart", as per RFC 1738) is used by that Store + * in a protocol dependent manner to locate and instantiate the + * appropriate Folder object.

    + * + * Note that RFC 1738 also specifies the syntax for the + * "schemepart" for IP-based protocols (IMAP4, POP3, etc.). + * Providers of IP-based mail Stores should implement that + * syntax for referring to Folders.

    + * + * @param url URLName that represents the desired folder + * @return Folder + * @see #getStore(URLName) + * @see javax.mail.URLName + * @exception NoSuchProviderException If a provider for the given + * URLName is not found. + * @exception MessagingException if the Folder could not be + * located or created. + */ + public Folder getFolder(URLName url) + throws MessagingException { + // First get the Store + Store store = getStore(url); + store.connect(); + return store.getFolder(url); + } + + /** + * Get a Transport object that implements this user's desired + * Transport protcol. The mail.transport.protocol property + * specifies the desired protocol. If an appropriate Transport + * object cannot be obtained, MessagingException is thrown. + * + * @return a Transport object + * @exception NoSuchProviderException If the provider is not found. + */ + public Transport getTransport() throws NoSuchProviderException { + String prot = getProperty("mail.transport.protocol"); + if (prot != null) + return getTransport(prot); + // if the property isn't set, use the protocol for "rfc822" + prot = (String)addressMap.get("rfc822"); + if (prot != null) + return getTransport(prot); + return getTransport("smtp"); // if all else fails + } + + /** + * Get a Transport object that implements the specified protocol. + * If an appropriate Transport object cannot be obtained, null is + * returned. + * + * @param protocol the Transport protocol + * @return a Transport object + * @exception NoSuchProviderException If provider for the given + * protocol is not found. + */ + public Transport getTransport(String protocol) + throws NoSuchProviderException { + return getTransport(new URLName(protocol, null, -1, null, null, null)); + } + + + /** + * Get a Transport object for the given URLName. If the requested + * Transport object cannot be obtained, NoSuchProviderException is thrown. + * + * The "scheme" part of the URL string (Refer RFC 1738) is used + * to locate the Transport protocol.

    + * + * @param url URLName that represents the desired Transport + * @return a closed Transport object + * @see javax.mail.URLName + * @exception NoSuchProviderException If a provider for the given + * URLName is not found. + */ + public Transport getTransport(URLName url) throws NoSuchProviderException { + String protocol = url.getProtocol(); + Provider p = getProvider(protocol); + return getTransport(p, url); + } + + /** + * Get an instance of the transport specified in the Provider. Instantiates + * the transport and returns it. + * + * @param provider Transport Provider that will be instantiated + * @return Instantiated Transport + * @exception NoSuchProviderException If provider for the given + * provider is not found. + */ + public Transport getTransport(Provider provider) + throws NoSuchProviderException { + return getTransport(provider, null); + } + + /** + * Get a Transport object that can transport a Message of the + * specified address type. + * + * @param address an address for which a Transport is needed + * @return A Transport object + * @see javax.mail.Address + * @exception NoSuchProviderException If provider for the + * Address type is not found + */ + public Transport getTransport(Address address) + throws NoSuchProviderException { + + String transportProtocol; + transportProtocol = + getProperty("mail.transport.protocol." + address.getType()); + if (transportProtocol != null) + return getTransport(transportProtocol); + transportProtocol = (String)addressMap.get(address.getType()); + if (transportProtocol != null) + return getTransport(transportProtocol); + throw new NoSuchProviderException("No provider for Address type: "+ + address.getType()); + } + + /** + * Get a Transport object using the given provider and urlname. + * + * @param provider the provider to use + * @param url urlname to use (can be null) + * @return A Transport object + * @exception NoSuchProviderException If no provider or the provider + * was the wrong class. + */ + + private Transport getTransport(Provider provider, URLName url) + throws NoSuchProviderException { + // make sure we have the correct type of provider + if (provider == null || provider.getType() != Provider.Type.TRANSPORT) { + throw new NoSuchProviderException("invalid provider"); + } + + return getService(provider, url, Transport.class); + } + + /** + * Get a Service object. Needs a provider object, but will + * create a URLName if needed. It attempts to instantiate + * the correct class. + * + * @param provider which provider to use + * @param url which URLName to use (can be null) + * @param type the service type (class) + * @exception NoSuchProviderException thrown when the class cannot be + * found or when it does not have the correct constructor + * (Session, URLName), or if it is not derived from + * Service. + */ + private T getService(Provider provider, URLName url, + Class type) + throws NoSuchProviderException { + // need a provider and url + if (provider == null) { + throw new NoSuchProviderException("null"); + } + + // create a url if needed + if (url == null) { + url = new URLName(provider.getProtocol(), null, -1, + null, null, null); + } + + Object service = null; + + // get the ClassLoader associated with the Authenticator + ClassLoader cl; + if (authenticator != null) + cl = authenticator.getClass().getClassLoader(); + else + cl = this.getClass().getClassLoader(); + + // now load the class + Class serviceClass = null; + try { + // First try the "application's" class loader. + ClassLoader ccl = getContextClassLoader(); + if (ccl != null) + try { + serviceClass = + Class.forName(provider.getClassName(), false, ccl); + } catch (ClassNotFoundException ex) { + // ignore it + } + if (serviceClass == null || !type.isAssignableFrom(serviceClass)) + serviceClass = + Class.forName(provider.getClassName(), false, cl); + + if (!type.isAssignableFrom(serviceClass)) + throw new ClassCastException( + type.getName() + " " + serviceClass.getName()); + } catch (Exception ex1) { + // That didn't work, now try the "system" class loader. + // (Need both of these because JDK 1.1 class loaders + // may not delegate to their parent class loader.) + try { + serviceClass = Class.forName(provider.getClassName()); + if (!type.isAssignableFrom(serviceClass)) + throw new ClassCastException( + type.getName() + " " + serviceClass.getName()); + } catch (Exception ex) { + // Nothing worked, give up. + logger.log(Level.FINE, "Exception loading provider", ex); + throw new NoSuchProviderException(provider.getProtocol()); + } + } + + // construct an instance of the class + try { + Class[] c = {javax.mail.Session.class, javax.mail.URLName.class}; + Constructor cons = serviceClass.getConstructor(c); + + Object[] o = {this, url}; + service = cons.newInstance(o); + + } catch (Exception ex) { + logger.log(Level.FINE, "Exception loading provider", ex); + throw new NoSuchProviderException(provider.getProtocol()); + } + + return type.cast(service); + } + + /** + * Save a PasswordAuthentication for this (store or transport) URLName. + * If pw is null the entry corresponding to the URLName is removed. + *

    + * This is normally used only by the store or transport implementations + * to allow authentication information to be shared among multiple + * uses of a session. + * + * @param url the URLName + * @param pw the PasswordAuthentication to save + */ + public void setPasswordAuthentication(URLName url, + PasswordAuthentication pw) { + if (pw == null) + authTable.remove(url); + else + authTable.put(url, pw); + } + + /** + * Return any saved PasswordAuthentication for this (store or transport) + * URLName. Normally used only by store or transport implementations. + * + * @param url the URLName + * @return the PasswordAuthentication corresponding to the URLName + */ + public PasswordAuthentication getPasswordAuthentication(URLName url) { + return authTable.get(url); + } + + /** + * Call back to the application to get the needed user name and password. + * The application should put up a dialog something like: + *

    +     * Connecting to <protocol> mail service on host <addr>, port <port>.
    +     * <prompt>
    +     *
    +     * User Name: <defaultUserName>
    +     * Password:
    +     * 
    + * + * @param addr InetAddress of the host. may be null. + * @param port the port on the host + * @param protocol protocol scheme (e.g. imap, pop3, etc.) + * @param prompt any additional String to show as part of + * the prompt; may be null. + * @param defaultUserName the default username. may be null. + * @return the authentication which was collected by the authenticator; + * may be null. + */ + public PasswordAuthentication requestPasswordAuthentication( + InetAddress addr, int port, + String protocol, String prompt, String defaultUserName) { + + if (authenticator != null) { + return authenticator.requestPasswordAuthentication( + addr, port, protocol, prompt, defaultUserName); + } else { + return null; + } + } + + /** + * Returns the Properties object associated with this Session + * + * @return Properties object + */ + public Properties getProperties() { + return props; + } + + /** + * Returns the value of the specified property. Returns null + * if this property does not exist. + * + * @param name the property name + * @return String that is the property value + */ + public String getProperty(String name) { + return props.getProperty(name); + } + + /** + * Get the Provider that uses the specified class name. + * + * @param className the class name + * @return the Provider + */ + private Provider getProviderByClassName(String className) { + // first, try our local list of providers + Provider p = providersByClassName.get(className); + if (p != null) + return p; + + // now, try services + ServiceLoader loader = ServiceLoader.load(Provider.class); + for (Provider pp : loader) { + if (className.equals(pp.getClassName())) + return pp; + } + + // finally, if we haven't loaded our config, load it and try again + if (!loadedProviders) { + loadProviders(true); + p = providersByClassName.get(className); + } + return p; + } + + /** + * Get the Provider for the specified protocol. + * + * @param protocol the protocol + * @return the Provider + */ + private Provider getProviderByProtocol(String protocol) { + // first, try our local list of providers + Provider p = providersByProtocol.get(protocol); + if (p != null) + return p; + + // now, try services + ServiceLoader loader = ServiceLoader.load(Provider.class); + for (Provider pp : loader) { + if (protocol.equals(pp.getProtocol())) + return pp; + } + + // finally, if we haven't loaded our config, load it and try again + if (!loadedProviders) { + loadProviders(true); + p = providersByProtocol.get(protocol); + } + return p; + } + + /** + * Load the protocol providers config files. + * If fallback is true, provide built in defaults if nothing is loaded. + */ + private void loadProviders(boolean fallback) { + StreamLoader loader = new StreamLoader() { + @Override + public void load(InputStream is) throws IOException { + loadProvidersFromStream(is); + } + }; + + // load system-wide javamail.providers from the + // /{conf,lib} directory + try { + if (confDir != null) + loadFile(confDir + "javamail.providers", loader); + } catch (SecurityException ex) {} + + // get the Class associated with the Authenticator + Class cl; + if (authenticator != null) + cl = authenticator.getClass(); + else + cl = this.getClass(); + + // load the META-INF/javamail.providers file supplied by an application + loadAllResources("META-INF/javamail.providers", cl, loader); + + // load default META-INF/javamail.default.providers from mail.jar file + loadResource("/META-INF/javamail.default.providers", cl, loader, false); + + /* + * If we haven't loaded any providers and the fallback configuration + * is needed, fake it. + */ + if ((providers == null || providers.size() == 0) && fallback) { + logger.config("failed to load any providers, using defaults"); + // failed to load any providers, initialize with our defaults + addProvider(new Provider(Provider.Type.STORE, + "imap", "com.sun.mail.imap.IMAPStore", + "Oracle", Version.version)); + addProvider(new Provider(Provider.Type.STORE, + "imaps", "com.sun.mail.imap.IMAPSSLStore", + "Oracle", Version.version)); + addProvider(new Provider(Provider.Type.STORE, + "pop3", "com.sun.mail.pop3.POP3Store", + "Oracle", Version.version)); + addProvider(new Provider(Provider.Type.STORE, + "pop3s", "com.sun.mail.pop3.POP3SSLStore", + "Oracle", Version.version)); + addProvider(new Provider(Provider.Type.TRANSPORT, + "smtp", "com.sun.mail.smtp.SMTPTransport", + "Oracle", Version.version)); + addProvider(new Provider(Provider.Type.TRANSPORT, + "smtps", "com.sun.mail.smtp.SMTPSSLTransport", + "Oracle", Version.version)); + } + + if (logger.isLoggable(Level.CONFIG)) { + // dump the output of the tables for debugging + logger.config("Tables of loaded providers from javamail.providers"); + logger.config("Providers Listed By Class Name: " + + providersByClassName.toString()); + logger.config("Providers Listed By Protocol: " + + providersByProtocol.toString()); + } + loadedProviders = true; + } + + private void loadProvidersFromStream(InputStream is) + throws IOException { + if (is != null) { + LineInputStream lis = new LineInputStream(is); + String currLine; + + // load and process one line at a time using LineInputStream + while ((currLine = lis.readLine()) != null) { + + if (currLine.startsWith("#")) + continue; + if (currLine.trim().length() == 0) + continue; // skip blank line + Provider.Type type = null; + String protocol = null, className = null; + String vendor = null, version = null; + + // separate line into key-value tuples + StringTokenizer tuples = new StringTokenizer(currLine,";"); + while (tuples.hasMoreTokens()) { + String currTuple = tuples.nextToken().trim(); + + // set the value of each attribute based on its key + int sep = currTuple.indexOf("="); + if (currTuple.startsWith("protocol=")) { + protocol = currTuple.substring(sep+1); + } else if (currTuple.startsWith("type=")) { + String strType = currTuple.substring(sep+1); + if (strType.equalsIgnoreCase("store")) { + type = Provider.Type.STORE; + } else if (strType.equalsIgnoreCase("transport")) { + type = Provider.Type.TRANSPORT; + } + } else if (currTuple.startsWith("class=")) { + className = currTuple.substring(sep+1); + } else if (currTuple.startsWith("vendor=")) { + vendor = currTuple.substring(sep+1); + } else if (currTuple.startsWith("version=")) { + version = currTuple.substring(sep+1); + } + } + + // check if a valid Provider; else, continue + if (type == null || protocol == null || className == null + || protocol.length() <= 0 || className.length() <= 0) { + + logger.log(Level.CONFIG, "Bad provider entry: {0}", + currLine); + continue; + } + Provider provider = new Provider(type, protocol, className, + vendor, version); + + // add the newly-created Provider to the lookup tables + addProvider(provider); + } + } + } + + /** + * Add a provider to the session. + * + * @param provider the provider to add + * @since JavaMail 1.4 + */ + public synchronized void addProvider(Provider provider) { + if (providers == null) + providers = new ArrayList(); + providers.add(provider); + providersByClassName.put(provider.getClassName(), provider); + if (!providersByProtocol.containsKey(provider.getProtocol())) + providersByProtocol.put(provider.getProtocol(), provider); + } + + // load maps in reverse order of preference so that the preferred + // map is loaded last since its entries will override the previous ones + private void loadAddressMap(Class cl) { + StreamLoader loader = new StreamLoader() { + @Override + public void load(InputStream is) throws IOException { + addressMap.load(is); + } + }; + + // load default META-INF/javamail.default.address.map from mail.jar + loadResource("/META-INF/javamail.default.address.map", cl, loader, true); + + // load the META-INF/javamail.address.map file supplied by an app + loadAllResources("META-INF/javamail.address.map", cl, loader); + + // load system-wide javamail.address.map from the + // /{conf,lib} directory + try { + if (confDir != null) + loadFile(confDir + "javamail.address.map", loader); + } catch (SecurityException ex) {} + + if (addressMap.isEmpty()) { + logger.config("failed to load address map, using defaults"); + addressMap.put("rfc822", "smtp"); + } + } + + /** + * Set the default transport protocol to use for addresses of + * the specified type. Normally the default is set by the + * javamail.default.address.map or + * javamail.address.map files or resources. + * + * @param addresstype type of address + * @param protocol name of protocol + * @see #getTransport(Address) + * @since JavaMail 1.4 + */ + public synchronized void setProtocolForAddress(String addresstype, + String protocol) { + if (protocol == null) + addressMap.remove(addresstype); + else + addressMap.put(addresstype, protocol); + } + + /** + * Load from the named file. + */ + private void loadFile(String name, StreamLoader loader) { + InputStream clis = null; + try { + clis = new BufferedInputStream(new FileInputStream(name)); + loader.load(clis); + logger.log(Level.CONFIG, "successfully loaded file: {0}", name); + } catch (FileNotFoundException fex) { + // ignore it + } catch (IOException e) { + if (logger.isLoggable(Level.CONFIG)) + logger.log(Level.CONFIG, "not loading file: " + name, e); + } catch (SecurityException sex) { + if (logger.isLoggable(Level.CONFIG)) + logger.log(Level.CONFIG, "not loading file: " + name, sex); + } finally { + try { + if (clis != null) + clis.close(); + } catch (IOException ex) { } // ignore it + } + } + + /** + * Load from the named resource. + */ + private void loadResource(String name, Class cl, StreamLoader loader, + boolean expected) { + InputStream clis = null; + try { + clis = getResourceAsStream(cl, name); + if (clis != null) { + loader.load(clis); + logger.log(Level.CONFIG, "successfully loaded resource: {0}", + name); + } else { + if (expected) + logger.log(Level.WARNING, + "expected resource not found: {0}", name); + } + } catch (IOException e) { + logger.log(Level.CONFIG, "Exception loading resource", e); + } catch (SecurityException sex) { + logger.log(Level.CONFIG, "Exception loading resource", sex); + } finally { + try { + if (clis != null) + clis.close(); + } catch (IOException ex) { } // ignore it + } + } + + /** + * Load all of the named resource. + */ + private void loadAllResources(String name, Class cl, + StreamLoader loader) { + boolean anyLoaded = false; + try { + URL[] urls; + ClassLoader cld = null; + // First try the "application's" class loader. + cld = getContextClassLoader(); + if (cld == null) + cld = cl.getClassLoader(); + if (cld != null) + urls = getResources(cld, name); + else + urls = getSystemResources(name); + if (urls != null) { + for (int i = 0; i < urls.length; i++) { + URL url = urls[i]; + InputStream clis = null; + logger.log(Level.CONFIG, "URL {0}", url); + try { + clis = openStream(url); + if (clis != null) { + loader.load(clis); + anyLoaded = true; + logger.log(Level.CONFIG, + "successfully loaded resource: {0}", url); + } else { + logger.log(Level.CONFIG, + "not loading resource: {0}", url); + } + } catch (FileNotFoundException fex) { + // ignore it + } catch (IOException ioex) { + logger.log(Level.CONFIG, "Exception loading resource", + ioex); + } catch (SecurityException sex) { + logger.log(Level.CONFIG, "Exception loading resource", + sex); + } finally { + try { + if (clis != null) + clis.close(); + } catch (IOException cex) { } + } + } + } + } catch (Exception ex) { + logger.log(Level.CONFIG, "Exception loading resource", ex); + } + + // if failed to load anything, fall back to old technique, just in case + if (!anyLoaded) { + /* + logger.config("!anyLoaded"); + */ + loadResource("/" + name, cl, loader, false); + } + } + + /* + * Following are security related methods that work on JDK 1.2 or newer. + */ + + static ClassLoader getContextClassLoader() { + return AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public ClassLoader run() { + ClassLoader cl = null; + try { + cl = Thread.currentThread().getContextClassLoader(); + } catch (SecurityException ex) { + } + return cl; + } + } + ); + } + + private static InputStream getResourceAsStream(final Class c, + final String name) throws IOException { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public InputStream run() throws IOException { + try { + return c.getResourceAsStream(name); + } catch (RuntimeException e) { + // gracefully handle ClassLoader bugs (Tomcat) + IOException ioex = new IOException( + "ClassLoader.getResourceAsStream failed"); + ioex.initCause(e); + throw ioex; + } + } + } + ); + } catch (PrivilegedActionException e) { + throw (IOException)e.getException(); + } + } + + private static URL[] getResources(final ClassLoader cl, final String name) { + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public URL[] run() { + URL[] ret = null; + try { + List v = Collections.list(cl.getResources(name)); + if (!v.isEmpty()) { + ret = new URL[v.size()]; + v.toArray(ret); + } + } catch (IOException ioex) { + } catch (SecurityException ex) { } + return ret; + } + }); + } + + private static URL[] getSystemResources(final String name) { + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public URL[] run() { + URL[] ret = null; + try { + List v = Collections.list( + ClassLoader.getSystemResources(name)); + if (!v.isEmpty()) { + ret = new URL[v.size()]; + v.toArray(ret); + } + } catch (IOException ioex) { + } catch (SecurityException ex) { } + return ret; + } + }); + } + + private static InputStream openStream(final URL url) throws IOException { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public InputStream run() throws IOException { + return url.openStream(); + } + } + ); + } catch (PrivilegedActionException e) { + throw (IOException)e.getException(); + } + } + + EventQueue getEventQueue() { + return q; + } +} + +/** + * Support interface to generalize + * code that loads resources from stream. + */ +interface StreamLoader { + public void load(InputStream is) throws IOException; +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Store.java b/fine-third-default/fine-mail/src/javax/mail/Store.java new file mode 100644 index 000000000..3e769c413 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Store.java @@ -0,0 +1,325 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.io.*; +import java.net.*; +import java.util.*; +import javax.mail.event.*; + +/** + * An abstract class that models a message store and its + * access protocol, for storing and retrieving messages. + * Subclasses provide actual implementations.

    + * + * Note that Store extends the Service + * class, which provides many common methods for naming stores, + * connecting to stores, and listening to connection events. + * + * @author John Mani + * @author Bill Shannon + * + * @see javax.mail.Service + * @see javax.mail.event.ConnectionEvent + * @see javax.mail.event.StoreEvent + */ + +public abstract class Store extends Service { + + /** + * Constructor. + * + * @param session Session object for this Store. + * @param urlname URLName object to be used for this Store + */ + protected Store(Session session, URLName urlname) { + super(session, urlname); + } + + /** + * Returns a Folder object that represents the 'root' of + * the default namespace presented to the user by the Store. + * + * @return the root Folder + * @exception IllegalStateException if this Store is not connected. + * @exception MessagingException for other failures + */ + public abstract Folder getDefaultFolder() throws MessagingException; + + /** + * Return the Folder object corresponding to the given name. Note + * that a Folder object is returned even if the named folder does + * not physically exist on the Store. The exists() + * method on the folder object indicates whether this folder really + * exists.

    + * + * Folder objects are not cached by the Store, so invoking this + * method on the same name multiple times will return that many + * distinct Folder objects. + * + * @param name The name of the Folder. In some Stores, name can + * be an absolute path if it starts with the + * hierarchy delimiter. Else it is interpreted + * relative to the 'root' of this namespace. + * @return Folder object + * @exception IllegalStateException if this Store is not connected. + * @exception MessagingException for other failures + * @see Folder#exists + * @see Folder#create + */ + public abstract Folder getFolder(String name) + throws MessagingException; + + /** + * Return a closed Folder object, corresponding to the given + * URLName. The store specified in the given URLName should + * refer to this Store object.

    + * + * Implementations of this method may obtain the name of the + * actual folder using the getFile() method on + * URLName, and use that name to create the folder. + * + * @param url URLName that denotes a folder + * @return Folder object + * @exception IllegalStateException if this Store is not connected. + * @exception MessagingException for other failures + * @see URLName + */ + public abstract Folder getFolder(URLName url) + throws MessagingException; + + /** + * Return a set of folders representing the personal namespaces + * for the current user. A personal namespace is a set of names that + * is considered within the personal scope of the authenticated user. + * Typically, only the authenticated user has access to mail folders + * in their personal namespace. If an INBOX exists for a user, it + * must appear within the user's personal namespace. In the + * typical case, there should be only one personal namespace for each + * user in each Store.

    + * + * This implementation returns an array with a single entry containing + * the return value of the getDefaultFolder method. + * Subclasses should override this method to return appropriate information. + * + * @return array of Folder objects + * @exception IllegalStateException if this Store is not connected. + * @exception MessagingException for other failures + * @since JavaMail 1.2 + */ + public Folder[] getPersonalNamespaces() throws MessagingException { + return new Folder[] { getDefaultFolder() }; + } + + /** + * Return a set of folders representing the namespaces for + * user. The namespaces returned represent the + * personal namespaces for the user. To access mail folders in the + * other user's namespace, the currently authenticated user must be + * explicitly granted access rights. For example, it is common for + * a manager to grant to their secretary access rights to their + * mail folders.

    + * + * This implementation returns an empty array. Subclasses should + * override this method to return appropriate information. + * + * @param user the user name + * @return array of Folder objects + * @exception IllegalStateException if this Store is not connected. + * @exception MessagingException for other failures + * @since JavaMail 1.2 + */ + public Folder[] getUserNamespaces(String user) + throws MessagingException { + return new Folder[0]; + } + + /** + * Return a set of folders representing the shared namespaces. + * A shared namespace is a namespace that consists of mail folders + * that are intended to be shared amongst users and do not exist + * within a user's personal namespace.

    + * + * This implementation returns an empty array. Subclasses should + * override this method to return appropriate information. + * + * @exception IllegalStateException if this Store is not connected. + * @exception MessagingException for other failures + * @return array of Folder objects + * @since JavaMail 1.2 + */ + public Folder[] getSharedNamespaces() throws MessagingException { + return new Folder[0]; + } + + // Vector of Store listeners + private volatile Vector storeListeners = null; + + /** + * Add a listener for StoreEvents on this Store.

    + * + * The default implementation provided here adds this listener + * to an internal list of StoreListeners. + * + * @param l the Listener for Store events + * @see javax.mail.event.StoreEvent + */ + public synchronized void addStoreListener(StoreListener l) { + if (storeListeners == null) + storeListeners = new Vector<>(); + storeListeners.addElement(l); + } + + /** + * Remove a listener for Store events.

    + * + * The default implementation provided here removes this listener + * from the internal list of StoreListeners. + * + * @param l the listener + * @see #addStoreListener + */ + public synchronized void removeStoreListener(StoreListener l) { + if (storeListeners != null) + storeListeners.removeElement(l); + } + + /** + * Notify all StoreListeners. Store implementations are + * expected to use this method to broadcast StoreEvents.

    + * + * The provided default implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the registered + * StoreListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param type the StoreEvent type + * @param message a message for the StoreEvent + */ + protected void notifyStoreListeners(int type, String message) { + if (storeListeners == null) + return; + + StoreEvent e = new StoreEvent(this, type, message); + queueEvent(e, storeListeners); + } + + // Vector of folder listeners + private volatile Vector folderListeners = null; + + /** + * Add a listener for Folder events on any Folder object + * obtained from this Store. FolderEvents are delivered to + * FolderListeners on the affected Folder as well as to + * FolderListeners on the containing Store.

    + * + * The default implementation provided here adds this listener + * to an internal list of FolderListeners. + * + * @param l the Listener for Folder events + * @see javax.mail.event.FolderEvent + */ + public synchronized void addFolderListener(FolderListener l) { + if (folderListeners == null) + folderListeners = new Vector<>(); + folderListeners.addElement(l); + } + + /** + * Remove a listener for Folder events.

    + * + * The default implementation provided here removes this listener + * from the internal list of FolderListeners. + * + * @param l the listener + * @see #addFolderListener + */ + public synchronized void removeFolderListener(FolderListener l) { + if (folderListeners != null) + folderListeners.removeElement(l); + } + + /** + * Notify all FolderListeners. Store implementations are + * expected to use this method to broadcast Folder events.

    + * + * The provided default implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the registered + * FolderListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param type type of FolderEvent + * @param folder affected Folder + * @see #notifyFolderRenamedListeners + */ + protected void notifyFolderListeners(int type, Folder folder) { + if (folderListeners == null) + return; + + FolderEvent e = new FolderEvent(this, folder, type); + queueEvent(e, folderListeners); + } + + /** + * Notify all FolderListeners about the renaming of a folder. + * Store implementations are expected to use this method to broadcast + * Folder events indicating the renaming of folders.

    + * + * The provided default implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the registered + * FolderListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param oldF the folder being renamed + * @param newF the folder representing the new name. + * @since JavaMail 1.1 + */ + protected void notifyFolderRenamedListeners(Folder oldF, Folder newF) { + if (folderListeners == null) + return; + + FolderEvent e = new FolderEvent(this, oldF, newF,FolderEvent.RENAMED); + queueEvent(e, folderListeners); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/StoreClosedException.java b/fine-third-default/fine-mail/src/javax/mail/StoreClosedException.java new file mode 100644 index 000000000..5559fade8 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/StoreClosedException.java @@ -0,0 +1,107 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * This exception is thrown when a method is invoked on a Messaging object + * and the Store that owns that object has died due to some reason. + * This exception should be treated as a fatal error; in particular any + * messaging object belonging to that Store must be considered invalid.

    + * + * The connect method may be invoked on the dead Store object to + * revive it.

    + * + * The getMessage() method returns more detailed information about the + * error that caused this exception.

    + * + * @author John Mani + */ + +public class StoreClosedException extends MessagingException { + transient private Store store; + + private static final long serialVersionUID = -3145392336120082655L; + + /** + * Constructs a StoreClosedException with no detail message. + * + * @param store The dead Store object + */ + public StoreClosedException(Store store) { + this(store, null); + } + + /** + * Constructs a StoreClosedException with the specified + * detail message. + * + * @param store The dead Store object + * @param message The detailed error message + */ + public StoreClosedException(Store store, String message) { + super(message); + this.store = store; + } + + /** + * Constructs a StoreClosedException with the specified + * detail message and embedded exception. The exception is chained + * to this exception. + * + * @param store The dead Store object + * @param message The detailed error message + * @param e The embedded exception + * @since JavaMail 1.5 + */ + public StoreClosedException(Store store, String message, Exception e) { + super(message, e); + this.store = store; + } + + /** + * Returns the dead Store object. + * + * @return the dead Store object + */ + public Store getStore() { + return store; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Transport.java b/fine-third-default/fine-mail/src/javax/mail/Transport.java new file mode 100644 index 000000000..8ee10c6a1 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Transport.java @@ -0,0 +1,424 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; +import javax.mail.event.*; + +/** + * An abstract class that models a message transport. + * Subclasses provide actual implementations.

    + * + * Note that Transport extends the Service + * class, which provides many common methods for naming transports, + * connecting to transports, and listening to connection events. + * + * @author John Mani + * @author Max Spivak + * @author Bill Shannon + * + * @see javax.mail.Service + * @see javax.mail.event.ConnectionEvent + * @see javax.mail.event.TransportEvent + */ + +public abstract class Transport extends Service { + + /** + * Constructor. + * + * @param session Session object for this Transport. + * @param urlname URLName object to be used for this Transport + */ + public Transport(Session session, URLName urlname) { + super(session, urlname); + } + + /** + * Send a message. The message will be sent to all recipient + * addresses specified in the message (as returned from the + * Message method getAllRecipients), + * using message transports appropriate to each address. The + * send method calls the saveChanges + * method on the message before sending it.

    + * + * If any of the recipient addresses is detected to be invalid by + * the Transport during message submission, a SendFailedException + * is thrown. Clients can get more detail about the failure by examining + * the exception. Whether or not the message is still sent successfully + * to any valid addresses depends on the Transport implementation. See + * SendFailedException for more details. Note also that success does + * not imply that the message was delivered to the ultimate recipient, + * as failures may occur in later stages of delivery. Once a Transport + * accepts a message for delivery to a recipient, failures that occur later + * should be reported to the user via another mechanism, such as + * returning the undeliverable message.

    + * + * In typical usage, a SendFailedException reflects an error detected + * by the server. The details of the SendFailedException will usually + * contain the error message from the server (such as an SMTP error + * message). An address may be detected as invalid for a variety of + * reasons - the address may not exist, the address may have invalid + * syntax, the address may have exceeded its quota, etc.

    + * + * Note that send is a static method that creates and + * manages its own connection. Any connection associated with any + * Transport instance used to invoke this method is ignored and not + * used. This method should only be invoked using the form + * Transport.send(msg);, and should never be invoked + * using an instance variable. + * + * @param msg the message to send + * @exception SendFailedException if the message could not + * be sent to some or any of the recipients. + * @exception MessagingException for other failures + * @see Message#saveChanges + * @see Message#getAllRecipients + * @see #send(Message, Address[]) + * @see javax.mail.SendFailedException + */ + public static void send(Message msg) throws MessagingException { + msg.saveChanges(); // do this first + send0(msg, msg.getAllRecipients(), null, null); + } + + /** + * Send the message to the specified addresses, ignoring any + * recipients specified in the message itself. The + * send method calls the saveChanges + * method on the message before sending it.

    + * + * @param msg the message to send + * @param addresses the addresses to which to send the message + * @exception SendFailedException if the message could not + * be sent to some or any of the recipients. + * @exception MessagingException for other failures + * @see Message#saveChanges + * @see #send(Message) + * @see javax.mail.SendFailedException + */ + public static void send(Message msg, Address[] addresses) + throws MessagingException { + + msg.saveChanges(); + send0(msg, addresses, null, null); + } + + /** + * Send a message. The message will be sent to all recipient + * addresses specified in the message (as returned from the + * Message method getAllRecipients). + * The send method calls the saveChanges + * method on the message before sending it.

    + * + * Use the specified user name and password to authenticate to + * the mail server. + * + * @param msg the message to send + * @param user the user name + * @param password this user's password + * @exception SendFailedException if the message could not + * be sent to some or any of the recipients. + * @exception MessagingException for other failures + * @see Message#saveChanges + * @see #send(Message) + * @see javax.mail.SendFailedException + * @since JavaMail 1.5 + */ + public static void send(Message msg, + String user, String password) throws MessagingException { + + msg.saveChanges(); + send0(msg, msg.getAllRecipients(), user, password); + } + + /** + * Send the message to the specified addresses, ignoring any + * recipients specified in the message itself. The + * send method calls the saveChanges + * method on the message before sending it.

    + * + * Use the specified user name and password to authenticate to + * the mail server. + * + * @param msg the message to send + * @param addresses the addresses to which to send the message + * @param user the user name + * @param password this user's password + * @exception SendFailedException if the message could not + * be sent to some or any of the recipients. + * @exception MessagingException for other failures + * @see Message#saveChanges + * @see #send(Message) + * @see javax.mail.SendFailedException + * @since JavaMail 1.5 + */ + public static void send(Message msg, Address[] addresses, + String user, String password) throws MessagingException { + + msg.saveChanges(); + send0(msg, addresses, user, password); + } + + // send, but without the saveChanges + private static void send0(Message msg, Address[] addresses, + String user, String password) throws MessagingException { + + if (addresses == null || addresses.length == 0) + throw new SendFailedException("No recipient addresses"); + + /* + * protocols is a map containing the addresses + * indexed by address type + */ + Map> protocols + = new HashMap<>(); + + // Lists of addresses + List

    invalid = new ArrayList<>(); + List
    validSent = new ArrayList<>(); + List
    validUnsent = new ArrayList<>(); + + for (int i = 0; i < addresses.length; i++) { + // is this address type already in the map? + if (protocols.containsKey(addresses[i].getType())) { + List
    v = protocols.get(addresses[i].getType()); + v.add(addresses[i]); + } else { + // need to add a new protocol + List
    w = new ArrayList<>(); + w.add(addresses[i]); + protocols.put(addresses[i].getType(), w); + } + } + + int dsize = protocols.size(); + if (dsize == 0) + throw new SendFailedException("No recipient addresses"); + + Session s = (msg.session != null) ? msg.session : + Session.getDefaultInstance(System.getProperties(), null); + Transport transport; + + /* + * Optimize the case of a single protocol. + */ + if (dsize == 1) { + transport = s.getTransport(addresses[0]); + try { + if (user != null) + transport.connect(user, password); + else + transport.connect(); + transport.sendMessage(msg, addresses); + } finally { + transport.close(); + } + return; + } + + /* + * More than one protocol. Have to do them one at a time + * and collect addresses and chain exceptions. + */ + MessagingException chainedEx = null; + boolean sendFailed = false; + + for(List
    v : protocols.values()) { + Address[] protaddresses = new Address[v.size()]; + v.toArray(protaddresses); + + // Get a Transport that can handle this address type. + if ((transport = s.getTransport(protaddresses[0])) == null) { + // Could not find an appropriate Transport .. + // Mark these addresses invalid. + for (int j = 0; j < protaddresses.length; j++) + invalid.add(protaddresses[j]); + continue; + } + try { + transport.connect(); + transport.sendMessage(msg, protaddresses); + } catch (SendFailedException sex) { + sendFailed = true; + // chain the exception we're catching to any previous ones + if (chainedEx == null) + chainedEx = sex; + else + chainedEx.setNextException(sex); + + // retrieve invalid addresses + Address[] a = sex.getInvalidAddresses(); + if (a != null) + for (int j = 0; j < a.length; j++) + invalid.add(a[j]); + + // retrieve validSent addresses + a = sex.getValidSentAddresses(); + if (a != null) + for (int k = 0; k < a.length; k++) + validSent.add(a[k]); + + // retrieve validUnsent addresses + Address[] c = sex.getValidUnsentAddresses(); + if (c != null) + for (int l = 0; l < c.length; l++) + validUnsent.add(c[l]); + } catch (MessagingException mex) { + sendFailed = true; + // chain the exception we're catching to any previous ones + if (chainedEx == null) + chainedEx = mex; + else + chainedEx.setNextException(mex); + } finally { + transport.close(); + } + } + + // done with all protocols. throw exception if something failed + if (sendFailed || invalid.size() != 0 || validUnsent.size() != 0) { + Address[] a = null, b = null, c = null; + + // copy address lists into arrays + if (validSent.size() > 0) { + a = new Address[validSent.size()]; + validSent.toArray(a); + } + if (validUnsent.size() > 0) { + b = new Address[validUnsent.size()]; + validUnsent.toArray(b); + } + if (invalid.size() > 0) { + c = new Address[invalid.size()]; + invalid.toArray(c); + } + throw new SendFailedException("Sending failed", chainedEx, + a, b, c); + } + } + + /** + * Send the Message to the specified list of addresses. An appropriate + * TransportEvent indicating the delivery status is delivered to any + * TransportListener registered on this Transport. Also, if any of + * the addresses is invalid, a SendFailedException is thrown. + * Whether or not the message is still sent succesfully to + * any valid addresses depends on the Transport implementation.

    + * + * Unlike the static send method, the sendMessage + * method does not call the saveChanges method on + * the message; the caller should do so. + * + * @param msg The Message to be sent + * @param addresses array of addresses to send this message to + * @see javax.mail.event.TransportEvent + * @exception SendFailedException if the send failed because of + * invalid addresses. + * @exception MessagingException if the connection is dead or not in the + * connected state + */ + public abstract void sendMessage(Message msg, Address[] addresses) + throws MessagingException; + + // Vector of Transport listeners + private volatile Vector transportListeners = null; + + /** + * Add a listener for Transport events.

    + * + * The default implementation provided here adds this listener + * to an internal list of TransportListeners. + * + * @param l the Listener for Transport events + * @see javax.mail.event.TransportEvent + */ + public synchronized void addTransportListener(TransportListener l) { + if (transportListeners == null) + transportListeners = new Vector<>(); + transportListeners.addElement(l); + } + + /** + * Remove a listener for Transport events.

    + * + * The default implementation provided here removes this listener + * from the internal list of TransportListeners. + * + * @param l the listener + * @see #addTransportListener + */ + public synchronized void removeTransportListener(TransportListener l) { + if (transportListeners != null) + transportListeners.removeElement(l); + } + + /** + * Notify all TransportListeners. Transport implementations are + * expected to use this method to broadcast TransportEvents.

    + * + * The provided default implementation queues the event into + * an internal event queue. An event dispatcher thread dequeues + * events from the queue and dispatches them to the registered + * TransportListeners. Note that the event dispatching occurs + * in a separate thread, thus avoiding potential deadlock problems. + * + * @param type the TransportEvent type + * @param validSent valid addresses to which message was sent + * @param validUnsent valid addresses to which message was not sent + * @param invalid the invalid addresses + * @param msg the message + */ + protected void notifyTransportListeners(int type, Address[] validSent, + Address[] validUnsent, + Address[] invalid, Message msg) { + if (transportListeners == null) + return; + + TransportEvent e = new TransportEvent(this, type, validSent, + validUnsent, invalid, msg); + queueEvent(e, transportListeners); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/UIDFolder.java b/fine-third-default/fine-mail/src/javax/mail/UIDFolder.java new file mode 100644 index 000000000..bbe3d01d5 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/UIDFolder.java @@ -0,0 +1,230 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.util.NoSuchElementException; + +/** + * The UIDFolder interface is implemented by Folders + * that can support the "disconnected" mode of operation, by providing + * unique-ids for messages in the folder. This interface is based on + * the IMAP model for supporting disconnected operation.

    + * + * A Unique identifier (UID) is a positive long value, assigned to + * each message in a specific folder. Unique identifiers are assigned + * in a strictly ascending fashion in the mailbox. + * That is, as each message is added to the mailbox it is assigned a + * higher UID than the message(s) which were added previously. Unique + * identifiers persist across sessions. This permits a client to + * resynchronize its state from a previous session with the server.

    + * + * Associated with every mailbox is a unique identifier validity value. + * If unique identifiers from an earlier session fail to persist to + * this session, the unique identifier validity value + * must be greater than the one used in the earlier + * session.

    + * + * Refer to RFC 2060 + * for more information. + * + * All the Folder objects returned by the default IMAP provider implement + * the UIDFolder interface. Use it as follows: + *

    + *
    + * 	Folder f = store.getFolder("whatever");
    + *	UIDFolder uf = (UIDFolder)f;
    + *	long uid = uf.getUID(msg);
    + *
    + * 

    + * + * @author Bill Shannon + * @author John Mani + */ + +public interface UIDFolder { + + /** + * A fetch profile item for fetching UIDs. + * This inner class extends the FetchProfile.Item + * class to add new FetchProfile item types, specific to UIDFolders. + * The only item currently defined here is the UID item. + * + * @see FetchProfile + */ + public static class FetchProfileItem extends FetchProfile.Item { + protected FetchProfileItem(String name) { + super(name); + } + + /** + * UID is a fetch profile item that can be included in a + * FetchProfile during a fetch request to a Folder. + * This item indicates that the UIDs for messages in the specified + * range are desired to be prefetched.

    + * + * An example of how a client uses this is below: + *

    +	 *
    +	 * 	FetchProfile fp = new FetchProfile();
    +	 *	fp.add(UIDFolder.FetchProfileItem.UID);
    +	 *	folder.fetch(msgs, fp);
    +	 *
    +	 * 
    + */ + public static final FetchProfileItem UID = + new FetchProfileItem("UID"); + } + + /** + * This is a special value that can be used as the end + * parameter in getMessagesByUID(start, end), to denote the + * UID of the last message in the folder. + * + * @see #getMessagesByUID + */ + public static final long LASTUID = -1; + + /** + * The largest value possible for a UID, a 32-bit unsigned integer. + * This can be used to fetch all new messages by keeping track of the + * last UID that was seen and using: + *
    +     *
    +     * 	Folder f = store.getFolder("whatever");
    +     *	UIDFolder uf = (UIDFolder)f;
    +     *	Message[] newMsgs =
    +     *		uf.getMessagesByUID(lastSeenUID + 1, UIDFolder.MAXUID);
    +     *
    +     * 

    + * + * @since JavaMail 1.6 + */ + public static final long MAXUID = 0xffffffffL; // max 32-bit unsigned int + + /** + * Returns the UIDValidity value associated with this folder.

    + * + * Clients typically compare this value against a UIDValidity + * value saved from a previous session to insure that any cached + * UIDs are not stale. + * + * @return UIDValidity + * @exception MessagingException for failures + */ + public long getUIDValidity() throws MessagingException; + + /** + * Get the Message corresponding to the given UID. If no such + * message exists, null is returned. + * + * @param uid UID for the desired message + * @return the Message object. null is returned + * if no message corresponding to this UID is obtained. + * @exception MessagingException for failures + */ + public Message getMessageByUID(long uid) throws MessagingException; + + /** + * Get the Messages specified by the given range. The special + * value LASTUID can be used for the end parameter + * to indicate the UID of the last message in the folder.

    + * + * Note that end need not be greater than start; + * the order of the range doesn't matter. + * Note also that, unless the folder is empty, use of LASTUID ensures + * that at least one message will be returned - the last message in the + * folder. + * + * @param start start UID + * @param end end UID + * @return array of Message objects + * @exception MessagingException for failures + * @see #LASTUID + */ + public Message[] getMessagesByUID(long start, long end) + throws MessagingException; + + /** + * Get the Messages specified by the given array of UIDs. If any UID is + * invalid, null is returned for that entry.

    + * + * Note that the returned array will be of the same size as the specified + * array of UIDs, and null entries may be present in the + * array to indicate invalid UIDs. + * + * @param uids array of UIDs + * @return array of Message objects + * @exception MessagingException for failures + */ + public Message[] getMessagesByUID(long[] uids) + throws MessagingException; + + /** + * Get the UID for the specified message. Note that the message + * must belong to this folder. Otherwise + * java.util.NoSuchElementException is thrown. + * + * @param message Message from this folder + * @return UID for this message + * @exception NoSuchElementException if the given Message + * is not in this Folder. + * @exception MessagingException for other failures + */ + public long getUID(Message message) throws MessagingException; + + /** + * Returns the predicted UID that will be assigned to the + * next message that is appended to this folder. + * Messages might be appended to the folder after this value + * is retrieved, causing this value to be out of date. + * This value might only be updated when a folder is first opened. + * Note that messages may have been appended to the folder + * while it was open and thus this value may be out of + * date.

    + * + * If the value is unknown, -1 is returned.

    + * + * @return the UIDNEXT value, or -1 if unknown + * @exception MessagingException for failures + * @since JavaMail 1.6 + */ + public long getUIDNext() throws MessagingException; +} diff --git a/fine-third-default/fine-mail/src/javax/mail/URLName.java b/fine-third-default/fine-mail/src/javax/mail/URLName.java new file mode 100644 index 000000000..1e28a32e9 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/URLName.java @@ -0,0 +1,810 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +import java.net.*; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.BitSet; +import java.util.Locale; + + +/** + * The name of a URL. This class represents a URL name and also + * provides the basic parsing functionality to parse most internet + * standard URL schemes.

    + * + * Note that this class differs from java.net.URL + * in that this class just represents the name of a URL, it does + * not model the connection to a URL. + * + * @author Christopher Cotton + * @author Bill Shannon + */ + +public class URLName { + + /** + * The full version of the URL + */ + protected String fullURL; + + /** + * The protocol to use (ftp, http, nntp, imap, pop3 ... etc.) . + */ + private String protocol; + + /** + * The username to use when connecting + */ + private String username; + + /** + * The password to use when connecting. + */ + private String password; + + /** + * The host name to which to connect. + */ + private String host; + + /** + * The host's IP address, used in equals and hashCode. + * Computed on demand. + */ + private InetAddress hostAddress; + private boolean hostAddressKnown = false; + + /** + * The protocol port to connect to. + */ + private int port = -1; + + /** + * The specified file name on that host. + */ + private String file; + + /** + * # reference. + */ + private String ref; + + /** + * Our hash code. + */ + private int hashCode = 0; + + /** + * A way to turn off encoding, just in case... + */ + private static boolean doEncode = true; + + static { + try { + doEncode = !Boolean.getBoolean("mail.URLName.dontencode"); + } catch (Exception ex) { + // ignore any errors + } + } + + /** + * Creates a URLName object from the specified protocol, + * host, port number, file, username, and password. Specifying a port + * number of -1 indicates that the URL should use the default port for + * the protocol. + * + * @param protocol the protocol + * @param host the host name + * @param port the port number + * @param file the file + * @param username the user name + * @param password the password + */ + public URLName( + String protocol, + String host, + int port, + String file, + String username, + String password + ) + { + this.protocol = protocol; + this.host = host; + this.port = port; + int refStart; + if (file != null && (refStart = file.indexOf('#')) != -1) { + this.file = file.substring(0, refStart); + this.ref = file.substring(refStart + 1); + } else { + this.file = file; + this.ref = null; + } + this.username = doEncode ? encode(username) : username; + this.password = doEncode ? encode(password) : password; + } + + /** + * Construct a URLName from a java.net.URL object. + * + * @param url the URL + */ + public URLName(URL url) { + this(url.toString()); + } + + /** + * Construct a URLName from the string. Parses out all the possible + * information (protocol, host, port, file, username, password). + * + * @param url the URL string + */ + public URLName(String url) { + parseString(url); + } + + /** + * Constructs a string representation of this URLName. + */ + @Override + public String toString() { + if (fullURL == null) { + // add the "protocol:" + StringBuilder tempURL = new StringBuilder(); + if (protocol != null) { + tempURL.append(protocol); + tempURL.append(":"); + } + + if (username != null || host != null) { + // add the "//" + tempURL.append("//"); + + // add the user:password@ + // XXX - can you just have a password? without a username? + if (username != null) { + tempURL.append(username); + + if (password != null){ + tempURL.append(":"); + tempURL.append(password); + } + + tempURL.append("@"); + } + + // add host + if (host != null) { + tempURL.append(host); + } + + // add port (if needed) + if (port != -1) { + tempURL.append(":"); + tempURL.append(Integer.toString(port)); + } + if (file != null) + tempURL.append("/"); + } + + // add the file + if (file != null) { + tempURL.append(file); + } + + // add the ref + if (ref != null) { + tempURL.append("#"); + tempURL.append(ref); + } + + // create the fullURL now + fullURL = tempURL.toString(); + } + + return fullURL; + } + + /** + * Method which does all of the work of parsing the string. + * + * @param url the URL string to parse + */ + protected void parseString(String url) { + // initialize everything in case called from subclass + // (URLName really should be a final class) + protocol = file = ref = host = username = password = null; + port = -1; + + int len = url.length(); + + // find the protocol + // XXX - should check for only legal characters before the colon + // (legal: a-z, A-Z, 0-9, "+", ".", "-") + int protocolEnd = url.indexOf(':'); + if (protocolEnd != -1) + protocol = url.substring(0, protocolEnd); + + // is this an Internet standard URL that contains a host name? + if (url.regionMatches(protocolEnd + 1, "//", 0, 2)) { + // find where the file starts + String fullhost = null; + int fileStart = url.indexOf('/', protocolEnd + 3); + if (fileStart != -1) { + fullhost = url.substring(protocolEnd + 3, fileStart); + if (fileStart + 1 < len) + file = url.substring(fileStart + 1); + else + file = ""; + } else + fullhost = url.substring(protocolEnd + 3); + + // examine the fullhost, for username password etc. + int i = fullhost.indexOf('@'); + if (i != -1) { + String fulluserpass = fullhost.substring(0, i); + fullhost = fullhost.substring(i + 1); + + // get user and password + int passindex = fulluserpass.indexOf(':'); + if (passindex != -1) { + username = fulluserpass.substring(0, passindex); + password = fulluserpass.substring(passindex + 1); + } else { + username = fulluserpass; + } + } + + // get the port (if there) + int portindex; + if (fullhost.length() > 0 && fullhost.charAt(0) == '[') { + // an IPv6 address? + portindex = fullhost.indexOf(':', fullhost.indexOf(']')); + } else { + portindex = fullhost.indexOf(':'); + } + if (portindex != -1) { + String portstring = fullhost.substring(portindex + 1); + if (portstring.length() > 0) { + try { + port = Integer.parseInt(portstring); + } catch (NumberFormatException nfex) { + port = -1; + } + } + + host = fullhost.substring(0, portindex); + } else { + host = fullhost; + } + } else { + if (protocolEnd + 1 < len) + file = url.substring(protocolEnd + 1); + } + + // extract the reference from the file name, if any + int refStart; + if (file != null && (refStart = file.indexOf('#')) != -1) { + ref = file.substring(refStart + 1); + file = file.substring(0, refStart); + } + } + + /** + * Returns the port number of this URLName. + * Returns -1 if the port is not set. + * + * @return the port number + */ + public int getPort() { + return port; + } + + /** + * Returns the protocol of this URLName. + * Returns null if this URLName has no protocol. + * + * @return the protocol + */ + public String getProtocol() { + return protocol; + } + + /** + * Returns the file name of this URLName. + * Returns null if this URLName has no file name. + * + * @return the file name of this URLName + */ + public String getFile() { + return file; + } + + /** + * Returns the reference of this URLName. + * Returns null if this URLName has no reference. + * + * @return the reference part of the URLName + */ + public String getRef() { + return ref; + } + + /** + * Returns the host of this URLName. + * Returns null if this URLName has no host. + * + * @return the host name + */ + public String getHost() { + return host; + } + + /** + * Returns the user name of this URLName. + * Returns null if this URLName has no user name. + * + * @return the user name + */ + public String getUsername() { + return doEncode ? decode(username) : username; + } + + /** + * Returns the password of this URLName. + * Returns null if this URLName has no password. + * + * @return the password + */ + public String getPassword() { + return doEncode ? decode(password) : password; + } + + /** + * Constructs a URL from the URLName. + * + * @return the URL + * @exception MalformedURLException if the URL is malformed + */ + public URL getURL() throws MalformedURLException { + // URL expects the file to include the separating "/" + String f = getFile(); + if (f == null) + f = ""; + else + f = "/" + f; + return new URL(getProtocol(), getHost(), getPort(), f); + } + + /** + * Compares two URLNames. The result is true if and only if the + * argument is not null and is a URLName object that represents the + * same URLName as this object. Two URLName objects are equal if + * they have the same protocol and the same host, + * the same port number on the host, the same username, + * and the same file on the host. The fields (host, username, + * file) are also considered the same if they are both + * null.

    + * + * Hosts are considered equal if the names are equal (case independent) + * or if host name lookups for them both succeed and they both reference + * the same IP address.

    + * + * Note that URLName has no knowledge of default port numbers for + * particular protocols, so "imap://host" and "imap://host:143" + * would not compare as equal.

    + * + * Note also that the password field is not included in the comparison, + * nor is any reference field appended to the filename. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof URLName)) + return false; + URLName u2 = (URLName)obj; + + // compare protocols + if (!(protocol == u2.protocol || + (protocol != null && protocol.equals(u2.protocol)))) + return false; + + // compare hosts + InetAddress a1 = getHostAddress(), a2 = u2.getHostAddress(); + // if we have internet address for both, and they're not the same, fail + if (a1 != null && a2 != null) { + if (!a1.equals(a2)) + return false; + // else, if we have host names for both, and they're not the same, fail + } else if (host != null && u2.host != null) { + if (!host.equalsIgnoreCase(u2.host)) + return false; + // else, if not both null + } else if (host != u2.host) { + return false; + } + // at this point, hosts match + + // compare usernames + if (!(username == u2.username || + (username != null && username.equals(u2.username)))) + return false; + + // Forget about password since it doesn't + // really denote a different store. + + // compare files + String f1 = file == null ? "" : file; + String f2 = u2.file == null ? "" : u2.file; + + if (!f1.equals(f2)) + return false; + + // compare ports + if (port != u2.port) + return false; + + // all comparisons succeeded, they're equal + return true; + } + + /** + * Compute the hash code for this URLName. + */ + @Override + public int hashCode() { + if (hashCode != 0) + return hashCode; + if (protocol != null) + hashCode += protocol.hashCode(); + InetAddress addr = getHostAddress(); + if (addr != null) + hashCode += addr.hashCode(); + else if (host != null) + hashCode += host.toLowerCase(Locale.ENGLISH).hashCode(); + if (username != null) + hashCode += username.hashCode(); + if (file != null) + hashCode += file.hashCode(); + hashCode += port; + return hashCode; + } + + /** + * Get the IP address of our host. Look up the + * name the first time and remember that we've done + * so, whether the lookup fails or not. + */ + private synchronized InetAddress getHostAddress() { + if (hostAddressKnown) + return hostAddress; + if (host == null) + return null; + try { + hostAddress = InetAddress.getByName(host); + } catch (UnknownHostException ex) { + hostAddress = null; + } + hostAddressKnown = true; + return hostAddress; + } + + /** + * The class contains a utility method for converting a + * String into a MIME format called + * "x-www-form-urlencoded" format. + *

    + * To convert a String, each character is examined in turn: + *

      + *
    • The ASCII characters 'a' through 'z', + * 'A' through 'Z', '0' + * through '9', and ".", "-", + * "*", "_" remain the same. + *
    • The space character ' ' is converted into a + * plus sign '+'. + *
    • All other characters are converted into the 3-character string + * "%xy", where xy is the two-digit + * hexadecimal representation of the lower 8-bits of the character. + *
    + * + * @author Herb Jellinek + * @since JDK1.0 + */ + static BitSet dontNeedEncoding; + static final int caseDiff = ('a' - 'A'); + + /* The list of characters that are not encoded have been determined by + referencing O'Reilly's "HTML: The Definitive Guide" (page 164). */ + + static { + dontNeedEncoding = new BitSet(256); + int i; + for (i = 'a'; i <= 'z'; i++) { + dontNeedEncoding.set(i); + } + for (i = 'A'; i <= 'Z'; i++) { + dontNeedEncoding.set(i); + } + for (i = '0'; i <= '9'; i++) { + dontNeedEncoding.set(i); + } + /* encoding a space to a + is done in the encode() method */ + dontNeedEncoding.set(' '); + dontNeedEncoding.set('-'); + dontNeedEncoding.set('_'); + dontNeedEncoding.set('.'); + dontNeedEncoding.set('*'); + } + + /** + * Translates a string into x-www-form-urlencoded format. + * + * @param s String to be translated. + * @return the translated String. + */ + static String encode(String s) { + if (s == null) + return null; + // the common case is no encoding is needed + for (int i = 0; i < s.length(); i++) { + int c = (int)s.charAt(i); + if (c == ' ' || !dontNeedEncoding.get(c)) + return _encode(s); + } + return s; + } + + private static String _encode(String s) { + int maxBytesPerChar = 10; + StringBuilder out = new StringBuilder(s.length()); + ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar); + OutputStreamWriter writer = new OutputStreamWriter(buf); + + for (int i = 0; i < s.length(); i++) { + int c = (int)s.charAt(i); + if (dontNeedEncoding.get(c)) { + if (c == ' ') { + c = '+'; + } + out.append((char)c); + } else { + // convert to external encoding before hex conversion + try { + writer.write(c); + writer.flush(); + } catch(IOException e) { + buf.reset(); + continue; + } + byte[] ba = buf.toByteArray(); + for (int j = 0; j < ba.length; j++) { + out.append('%'); + char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16); + // converting to use uppercase letter as part of + // the hex value if ch is a letter. + if (Character.isLetter(ch)) { + ch -= caseDiff; + } + out.append(ch); + ch = Character.forDigit(ba[j] & 0xF, 16); + if (Character.isLetter(ch)) { + ch -= caseDiff; + } + out.append(ch); + } + buf.reset(); + } + } + + return out.toString(); + } + + + /** + * The class contains a utility method for converting from + * a MIME format called "x-www-form-urlencoded" + * to a String + *

    + * To convert to a String, each character is examined in turn: + *

      + *
    • The ASCII characters 'a' through 'z', + * 'A' through 'Z', and '0' + * through '9' remain the same. + *
    • The plus sign '+'is converted into a + * space character ' '. + *
    • The remaining characters are represented by 3-character + * strings which begin with the percent sign, + * "%xy", where xy is the two-digit + * hexadecimal representation of the lower 8-bits of the character. + *
    + * + * @author Mark Chamness + * @author Michael McCloskey + * @since 1.2 + */ + + /** + * Decodes a "x-www-form-urlencoded" + * to a String. + * @param s the String to decode + * @return the newly decoded String + */ + static String decode(String s) { + if (s == null) + return null; + if (indexOfAny(s, "+%") == -1) + return s; // the common case + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + switch (c) { + case '+': + sb.append(' '); + break; + case '%': + try { + sb.append((char)Integer.parseInt( + s.substring(i+1,i+3),16)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + "Illegal URL encoded value: " + + s.substring(i,i+3)); + } + i += 2; + break; + default: + sb.append(c); + break; + } + } + // Undo conversion to external encoding + String result = sb.toString(); + try { + byte[] inputBytes = result.getBytes("8859_1"); + result = new String(inputBytes); + } catch (UnsupportedEncodingException e) { + // The system should always have 8859_1 + } + return result; + } + + /** + * Return the first index of any of the characters in "any" in "s", + * or -1 if none are found. + * + * This should be a method on String. + */ + private static int indexOfAny(String s, String any) { + return indexOfAny(s, any, 0); + } + + private static int indexOfAny(String s, String any, int start) { + try { + int len = s.length(); + for (int i = start; i < len; i++) { + if (any.indexOf(s.charAt(i)) >= 0) + return i; + } + return -1; + } catch (StringIndexOutOfBoundsException e) { + return -1; + } + } + + /* + // Do not remove, this is needed when testing new URL cases + public static void main(String[] argv) { + String [] testURLNames = { + "protocol://userid:password@host:119/file", + "http://funny/folder/file.html", + "http://funny/folder/file.html#ref", + "http://funny/folder/file.html#", + "http://funny/#ref", + "imap://jmr:secret@labyrinth//var/mail/jmr", + "nntp://fred@labyrinth:143/save/it/now.mbox", + "imap://jmr@labyrinth/INBOX", + "imap://labryrinth", + "imap://labryrinth/", + "file:", + "file:INBOX", + "file:/home/shannon/mail/foo", + "/tmp/foo", + "//host/tmp/foo", + ":/tmp/foo", + "/really/weird:/tmp/foo#bar", + "" + }; + + URLName url = + new URLName("protocol", "host", 119, "file", "userid", "password"); + System.out.println("Test URL: " + url.toString()); + if (argv.length == 0) { + for (int i = 0; i < testURLNames.length; i++) { + print(testURLNames[i]); + System.out.println(); + } + } else { + for (int i = 0; i < argv.length; i++) { + print(argv[i]); + System.out.println(); + } + if (argv.length == 2) { + URLName u1 = new URLName(argv[0]); + URLName u2 = new URLName(argv[1]); + System.out.println("URL1 hash code: " + u1.hashCode()); + System.out.println("URL2 hash code: " + u2.hashCode()); + if (u1.equals(u2)) + System.out.println("success, equal"); + else + System.out.println("fail, not equal"); + if (u2.equals(u1)) + System.out.println("success, equal"); + else + System.out.println("fail, not equal"); + if (u1.hashCode() == u2.hashCode()) + System.out.println("success, hashCodes equal"); + else + System.out.println("fail, hashCodes not equal"); + } + } + } + + private static void print(String name) { + URLName url = new URLName(name); + System.out.println("Original URL: " + name); + System.out.println("The fullUrl : " + url.toString()); + if (!name.equals(url.toString())) + System.out.println(" : NOT EQUAL!"); + System.out.println("The protocol is: " + url.getProtocol()); + System.out.println("The host is: " + url.getHost()); + System.out.println("The port is: " + url.getPort()); + System.out.println("The user is: " + url.getUsername()); + System.out.println("The password is: " + url.getPassword()); + System.out.println("The file is: " + url.getFile()); + System.out.println("The ref is: " + url.getRef()); + } + */ +} diff --git a/fine-third-default/fine-mail/src/javax/mail/Version.java b/fine-third-default/fine-mail/src/javax/mail/Version.java new file mode 100644 index 000000000..7dc5b11ee --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/Version.java @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail; + +/** + * Package-private class that defines the version of JavaMail. + * This file is a template for the class that is generated + * at build time. + */ +class Version { + public static final String version = "${mail.version}"; +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/ConnectionAdapter.java b/fine-third-default/fine-mail/src/javax/mail/event/ConnectionAdapter.java new file mode 100644 index 000000000..eff1b2cac --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/ConnectionAdapter.java @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +/** + * The adapter which receives connection events. + * The methods in this class are empty; this class is provided as a + * convenience for easily creating listeners by extending this class + * and overriding only the methods of interest. + * + * @author John Mani + */ +public abstract class ConnectionAdapter implements ConnectionListener { + @Override + public void opened(ConnectionEvent e) {} + @Override + public void disconnected(ConnectionEvent e) {} + @Override + public void closed(ConnectionEvent e) {} +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/ConnectionEvent.java b/fine-third-default/fine-mail/src/javax/mail/event/ConnectionEvent.java new file mode 100644 index 000000000..38f335626 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/ConnectionEvent.java @@ -0,0 +1,101 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; +import javax.mail.*; + +/** + * This class models Connection events. + * + * @author John Mani + */ + +public class ConnectionEvent extends MailEvent { + + /** A connection was opened. */ + public static final int OPENED = 1; + /** A connection was disconnected (not currently used). */ + public static final int DISCONNECTED = 2; + /** A connection was closed. */ + public static final int CLOSED = 3; + + /** + * The event type. + * + * @serial + */ + protected int type; + + private static final long serialVersionUID = -1855480171284792957L; + + /** + * Construct a ConnectionEvent. + * + * @param source The source object + * @param type the event type + */ + public ConnectionEvent(Object source, int type) { + super(source); + this.type = type; + } + + /** + * Return the type of this event + * @return type + */ + public int getType() { + return type; + } + + /** + * Invokes the appropriate ConnectionListener method + */ + @Override + public void dispatch(Object listener) { + if (type == OPENED) + ((ConnectionListener)listener).opened(this); + else if (type == DISCONNECTED) + ((ConnectionListener)listener).disconnected(this); + else if (type == CLOSED) + ((ConnectionListener)listener).closed(this); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/ConnectionListener.java b/fine-third-default/fine-mail/src/javax/mail/event/ConnectionListener.java new file mode 100644 index 000000000..d6f96b530 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/ConnectionListener.java @@ -0,0 +1,74 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; + +/** + * This is the Listener interface for Connection events. + * + * @author John Mani + */ + +public interface ConnectionListener extends java.util.EventListener { + + /** + * Invoked when a Store/Folder/Transport is opened. + * + * @param e the ConnectionEvent + */ + public void opened(ConnectionEvent e); + + /** + * Invoked when a Store is disconnected. Note that a folder + * cannot be disconnected, so a folder will not fire this event + * + * @param e the ConnectionEvent + */ + public void disconnected(ConnectionEvent e); + + /** + * Invoked when a Store/Folder/Transport is closed. + * + * @param e the ConnectionEvent + */ + public void closed(ConnectionEvent e); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/FolderAdapter.java b/fine-third-default/fine-mail/src/javax/mail/event/FolderAdapter.java new file mode 100644 index 000000000..057bad560 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/FolderAdapter.java @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +/** + * The adapter which receives Folder events. + * The methods in this class are empty; this class is provided as a + * convenience for easily creating listeners by extending this class + * and overriding only the methods of interest. + * + * @author John Mani + */ +public abstract class FolderAdapter implements FolderListener { + @Override + public void folderCreated(FolderEvent e) {} + @Override + public void folderRenamed(FolderEvent e) {} + @Override + public void folderDeleted(FolderEvent e) {} +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/FolderEvent.java b/fine-third-default/fine-mail/src/javax/mail/event/FolderEvent.java new file mode 100644 index 000000000..f637db439 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/FolderEvent.java @@ -0,0 +1,168 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; +import javax.mail.*; + +/** + * This class models Folder existence events. FolderEvents are + * delivered to FolderListeners registered on the affected Folder as + * well as the containing Store.

    + * + * Service providers vary widely in their ability to notify clients of + * these events. At a minimum, service providers must notify listeners + * registered on the same Store or Folder object on which the operation + * occurs. Service providers may also notify listeners when changes + * are made through operations on other objects in the same virtual + * machine, or by other clients in the same or other hosts. Such + * notifications are not required and are typically not supported + * by mail protocols (including IMAP). + * + * @author John Mani + * @author Bill Shannon + */ + +public class FolderEvent extends MailEvent { + + /** The folder was created. */ + public static final int CREATED = 1; + /** The folder was deleted. */ + public static final int DELETED = 2; + /** The folder was renamed. */ + public static final int RENAMED = 3; + + /** + * The event type. + * + * @serial + */ + protected int type; + + /** + * The folder the event occurred on. + */ + transient protected Folder folder; + + /** + * The folder that represents the new name, in case of a RENAMED event. + * + * @since JavaMail 1.1 + */ + transient protected Folder newFolder; + + private static final long serialVersionUID = 5278131310563694307L; + + /** + * Constructor.

    + * + * @param source The source of the event + * @param folder The affected folder + * @param type The event type + */ + public FolderEvent(Object source, Folder folder, int type) { + this(source, folder, folder, type); + } + + /** + * Constructor. Use for RENAMED events. + * + * @param source The source of the event + * @param oldFolder The folder that is renamed + * @param newFolder The folder that represents the new name + * @param type The event type + * @since JavaMail 1.1 + */ + public FolderEvent(Object source, Folder oldFolder, + Folder newFolder, int type) { + super(source); + this.folder = oldFolder; + this.newFolder = newFolder; + this.type = type; + } + + /** + * Return the type of this event. + * + * @return type + */ + public int getType() { + return type; + } + + /** + * Return the affected folder. + * + * @return the affected folder + * @see #getNewFolder + */ + public Folder getFolder() { + return folder; + } + + /** + * If this event indicates that a folder is renamed, (i.e, the event type + * is RENAMED), then this method returns the Folder object representing the + * new name.

    + * + * The getFolder() method returns the folder that is renamed. + * + * @return Folder representing the new name. + * @see #getFolder + * @since JavaMail 1.1 + */ + public Folder getNewFolder() { + return newFolder; + } + + /** + * Invokes the appropriate FolderListener method + */ + @Override + public void dispatch(Object listener) { + if (type == CREATED) + ((FolderListener)listener).folderCreated(this); + else if (type == DELETED) + ((FolderListener)listener).folderDeleted(this); + else if (type == RENAMED) + ((FolderListener)listener).folderRenamed(this); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/FolderListener.java b/fine-third-default/fine-mail/src/javax/mail/event/FolderListener.java new file mode 100644 index 000000000..d5ca4b653 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/FolderListener.java @@ -0,0 +1,72 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; + +/** + * This is the Listener interface for Folder events. + * + * @author John Mani + */ + +public interface FolderListener extends java.util.EventListener { + /** + * Invoked when a Folder is created. + * + * @param e the FolderEvent + */ + public void folderCreated(FolderEvent e); + + /** + * Invoked when a folder is deleted. + * + * @param e the FolderEvent + */ + public void folderDeleted(FolderEvent e); + + /** + * Invoked when a folder is renamed. + * + * @param e the FolderEvent + */ + public void folderRenamed(FolderEvent e); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/MailEvent.java b/fine-third-default/fine-mail/src/javax/mail/event/MailEvent.java new file mode 100644 index 000000000..682b8e0c9 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/MailEvent.java @@ -0,0 +1,70 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.EventObject; + +/** + * Common base class for mail events, defining the dispatch method. + * + * @author Bill Shannon + */ + +public abstract class MailEvent extends EventObject { + private static final long serialVersionUID = 1846275636325456631L; + + /** + * Construct a MailEvent referring to the given source. + * + * @param source the source of the event + */ + public MailEvent(Object source) { + super(source); + } + + /** + * This method invokes the appropriate method on a listener for + * this event. Subclasses provide the implementation. + * + * @param listener the listener to invoke on + */ + public abstract void dispatch(Object listener); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/MessageChangedEvent.java b/fine-third-default/fine-mail/src/javax/mail/event/MessageChangedEvent.java new file mode 100644 index 000000000..be9f1ec40 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/MessageChangedEvent.java @@ -0,0 +1,108 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; +import javax.mail.*; + +/** + * This class models Message change events. + * + * @author John Mani + */ + +public class MessageChangedEvent extends MailEvent { + + /** The message's flags changed. */ + public static final int FLAGS_CHANGED = 1; + /** The message's envelope (headers, but not body) changed. */ + public static final int ENVELOPE_CHANGED = 2; + + /** + * The event type. + * + * @serial + */ + protected int type; + + /** + * The message that changed. + */ + transient protected Message msg; + + private static final long serialVersionUID = -4974972972105535108L; + + /** + * Constructor. + * @param source The folder that owns the message + * @param type The change type + * @param msg The changed message + */ + public MessageChangedEvent(Object source, int type, Message msg) { + super(source); + this.msg = msg; + this.type = type; + } + + /** + * Return the type of this event. + * @return type + */ + public int getMessageChangeType() { + return type; + } + + /** + * Return the changed Message. + * @return the message + */ + public Message getMessage() { + return msg; + } + + /** + * Invokes the appropriate MessageChangedListener method. + */ + @Override + public void dispatch(Object listener) { + ((MessageChangedListener)listener).messageChanged(this); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/MessageChangedListener.java b/fine-third-default/fine-mail/src/javax/mail/event/MessageChangedListener.java new file mode 100644 index 000000000..608d3af3e --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/MessageChangedListener.java @@ -0,0 +1,61 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; + +/** + * This is the Listener interface for MessageChanged events + * + * @author John Mani + */ + +public interface MessageChangedListener extends java.util.EventListener { + /** + * Invoked when a message is changed. The change-type specifies + * what changed. + * + * @param e the MessageChangedEvent + * @see MessageChangedEvent#FLAGS_CHANGED + * @see MessageChangedEvent#ENVELOPE_CHANGED + */ + public void messageChanged(MessageChangedEvent e); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/MessageCountAdapter.java b/fine-third-default/fine-mail/src/javax/mail/event/MessageCountAdapter.java new file mode 100644 index 000000000..8972dd503 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/MessageCountAdapter.java @@ -0,0 +1,56 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +/** + * The adapter which receives MessageCount events. + * The methods in this class are empty; this class is provided as a + * convenience for easily creating listeners by extending this class + * and overriding only the methods of interest. + * + * @author John Mani + */ +public abstract class MessageCountAdapter implements MessageCountListener { + @Override + public void messagesAdded(MessageCountEvent e) {} + @Override + public void messagesRemoved(MessageCountEvent e) {} +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/MessageCountEvent.java b/fine-third-default/fine-mail/src/javax/mail/event/MessageCountEvent.java new file mode 100644 index 000000000..0feac7ed1 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/MessageCountEvent.java @@ -0,0 +1,158 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; +import javax.mail.*; + +/** + * This class notifies changes in the number of messages in a folder.

    + * + * Note that some folder types may only deliver MessageCountEvents at + * certain times or after certain operations. IMAP in particular will + * only notify the client of MessageCountEvents when a client issues a + * new command. Refer to + * RFC 3501 + * for details. + * A client may want to "poll" the folder by occasionally calling the + * {@link javax.mail.Folder#getMessageCount getMessageCount} or + * {@link javax.mail.Folder#isOpen isOpen} methods + * to solicit any such notifications. + * + * @author John Mani + */ + +public class MessageCountEvent extends MailEvent { + + /** The messages were added to their folder */ + public static final int ADDED = 1; + /** The messages were removed from their folder */ + public static final int REMOVED = 2; + + /** + * The event type. + * + * @serial + */ + protected int type; + + /** + * If true, this event is the result of an explicit + * expunge by this client, and the messages in this + * folder have been renumbered to account for this. + * If false, this event is the result of an expunge + * by external sources. + * + * @serial + */ + protected boolean removed; + + /** + * The messages. + */ + transient protected Message[] msgs; + + private static final long serialVersionUID = -7447022340837897369L; + + /** + * Constructor. + * @param folder The containing folder + * @param type The event type + * @param removed If true, this event is the result of an explicit + * expunge by this client, and the messages in this + * folder have been renumbered to account for this. + * If false, this event is the result of an expunge + * by external sources. + * + * @param msgs The messages added/removed + */ + public MessageCountEvent(Folder folder, int type, + boolean removed, Message[] msgs) { + super(folder); + this.type = type; + this.removed = removed; + this.msgs = msgs; + } + + /** + * Return the type of this event. + * @return type + */ + public int getType() { + return type; + } + + /** + * Indicates whether this event is the result of an explicit + * expunge by this client, or due to an expunge from external + * sources. If true, this event is due to an + * explicit expunge and hence all remaining messages in this + * folder have been renumbered. If false, this event + * is due to an external expunge.

    + * + * Note that this method is valid only if the type of this event + * is REMOVED + * + * @return true if the message has been removed + */ + public boolean isRemoved() { + return removed; + } + + /** + * Return the array of messages added or removed. + * @return array of messages + */ + public Message[] getMessages() { + return msgs; + } + + /** + * Invokes the appropriate MessageCountListener method. + */ + @Override + public void dispatch(Object listener) { + if (type == ADDED) + ((MessageCountListener)listener).messagesAdded(this); + else // REMOVED + ((MessageCountListener)listener).messagesRemoved(this); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/MessageCountListener.java b/fine-third-default/fine-mail/src/javax/mail/event/MessageCountListener.java new file mode 100644 index 000000000..40e38e52e --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/MessageCountListener.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; + +/** + * This is the Listener interface for MessageCount events. + * + * @author John Mani + */ + +public interface MessageCountListener extends java.util.EventListener { + /** + * Invoked when messages are added into a folder. + * + * @param e the MessageCountEvent + */ + public void messagesAdded(MessageCountEvent e); + + /** + * Invoked when messages are removed (expunged) from a folder. + * + * @param e the MessageCountEvent + */ + public void messagesRemoved(MessageCountEvent e); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/StoreEvent.java b/fine-third-default/fine-mail/src/javax/mail/event/StoreEvent.java new file mode 100644 index 000000000..288483f23 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/StoreEvent.java @@ -0,0 +1,123 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; +import javax.mail.*; + +/** + * This class models notifications from the Store connection. These + * notifications can be ALERTS or NOTICES. ALERTS must be presented + * to the user in a fashion that calls the user's attention to the + * message. + * + * @author John Mani + */ + +public class StoreEvent extends MailEvent { + + /** + * Indicates that this message is an ALERT. + */ + public static final int ALERT = 1; + + /** + * Indicates that this message is a NOTICE. + */ + public static final int NOTICE = 2; + + /** + * The event type. + * + * @serial + */ + protected int type; + + /** + * The message text to be presented to the user. + * + * @serial + */ + protected String message; + + private static final long serialVersionUID = 1938704919992515330L; + + /** + * Construct a StoreEvent. + * + * @param store the source Store + * @param type the event type + * @param message a message assoicated with the event + */ + public StoreEvent(Store store, int type, String message) { + super(store); + this.type = type; + this.message = message; + } + + /** + * Return the type of this event. + * + * @return type + * @see #ALERT + * @see #NOTICE + */ + public int getMessageType() { + return type; + } + + /** + * Get the message from the Store. + * + * @return message from the Store + */ + public String getMessage() { + return message; + } + + /** + * Invokes the appropriate StoreListener method. + */ + @Override + public void dispatch(Object listener) { + ((StoreListener)listener).notification(this); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/StoreListener.java b/fine-third-default/fine-mail/src/javax/mail/event/StoreListener.java new file mode 100644 index 000000000..47ca884a3 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/StoreListener.java @@ -0,0 +1,61 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; + +/** + * This is the Listener interface for Store Notifications. + * + * @author John Mani + */ + +public interface StoreListener extends java.util.EventListener { + + /** + * Invoked when the Store generates a notification event. + * + * @param e the StoreEvent + * @see StoreEvent#ALERT + * @see StoreEvent#NOTICE + */ + public void notification(StoreEvent e); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/TransportAdapter.java b/fine-third-default/fine-mail/src/javax/mail/event/TransportAdapter.java new file mode 100644 index 000000000..4f2ba8aca --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/TransportAdapter.java @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +/** + * The adapter which receives Transport events. + * The methods in this class are empty; this class is provided as a + * convenience for easily creating listeners by extending this class + * and overriding only the methods of interest. + * + * @author John Mani + */ +public abstract class TransportAdapter implements TransportListener { + @Override + public void messageDelivered(TransportEvent e) {} + @Override + public void messageNotDelivered(TransportEvent e) {} + @Override + public void messagePartiallyDelivered(TransportEvent e) {} +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/TransportEvent.java b/fine-third-default/fine-mail/src/javax/mail/event/TransportEvent.java new file mode 100644 index 000000000..96fca48bd --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/TransportEvent.java @@ -0,0 +1,178 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; +import javax.mail.*; + +/** + * This class models Transport events. + * + * @author John Mani + * @author Max Spivak + * + * @see javax.mail.Transport + * @see javax.mail.event.TransportListener + */ + +public class TransportEvent extends MailEvent { + + /** + * Message has been successfully delivered to all recipients by the + * transport firing this event. validSent[] contains all the addresses + * this transport sent to successfully. validUnsent[] and invalid[] + * should be null, + */ + public static final int MESSAGE_DELIVERED = 1; + + /** + * Message was not sent for some reason. validSent[] should be null. + * validUnsent[] may have addresses that are valid (but the message + * wasn't sent to them). invalid[] should likely contain invalid addresses. + */ + public static final int MESSAGE_NOT_DELIVERED = 2; + + /** + * Message was successfully sent to some recipients but not to all. + * validSent[] holds addresses of recipients to whom the message was sent. + * validUnsent[] holds valid addresses to which the message was not sent. + * invalid[] holds invalid addresses, if any. + */ + public static final int MESSAGE_PARTIALLY_DELIVERED = 3; + + + /** + * The event type. + * + * @serial + */ + protected int type; + + /** The valid address to which the message was sent. */ + transient protected Address[] validSent; + /** The valid address to which the message was not sent. */ + transient protected Address[] validUnsent; + /** The invalid addresses. */ + transient protected Address[] invalid; + /** The Message to which this event applies. */ + transient protected Message msg; + + private static final long serialVersionUID = -4729852364684273073L; + + /** + * Constructor. + * + * @param transport The Transport object + * @param type the event type (MESSAGE_DELIVERED, etc.) + * @param validSent the valid addresses to which the message was sent + * @param validUnsent the valid addresses to which the message was + * not sent + * @param invalid the invalid addresses + * @param msg the message being sent + */ + public TransportEvent(Transport transport, int type, Address[] validSent, + Address[] validUnsent, Address[] invalid, + Message msg) { + super(transport); + this.type = type; + this.validSent = validSent; + this.validUnsent = validUnsent; + this.invalid = invalid; + this.msg = msg; + } + + /** + * Return the type of this event. + * @return type + */ + public int getType() { + return type; + } + + /** + * Return the addresses to which this message was sent succesfully. + * @return Addresses to which the message was sent successfully or null + */ + public Address[] getValidSentAddresses() { + return validSent; + } + + /** + * Return the addresses that are valid but to which this message + * was not sent. + * @return Addresses that are valid but to which the message was + * not sent successfully or null + */ + public Address[] getValidUnsentAddresses() { + return validUnsent; + } + + /** + * Return the addresses to which this message could not be sent. + * @return Addresses to which the message sending failed or null + */ + public Address[] getInvalidAddresses() { + return invalid; + } + + /** + * Get the Message object associated with this Transport Event. + * + * @return the Message object + * @since JavaMail 1.2 + */ + public Message getMessage() { + return msg; + } + + /** + * Invokes the appropriate TransportListener method. + */ + @Override + public void dispatch(Object listener) { + if (type == MESSAGE_DELIVERED) + ((TransportListener)listener).messageDelivered(this); + else if (type == MESSAGE_NOT_DELIVERED) + ((TransportListener)listener).messageNotDelivered(this); + else // MESSAGE_PARTIALLY_DELIVERED + ((TransportListener)listener).messagePartiallyDelivered(this); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/TransportListener.java b/fine-third-default/fine-mail/src/javax/mail/event/TransportListener.java new file mode 100644 index 000000000..86e86280e --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/TransportListener.java @@ -0,0 +1,76 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.event; + +import java.util.*; + +/** + * This is the Listener interface for Transport events + * + * @author John Mani + * @author Max Spivak + * + * @see javax.mail.Transport + * @see javax.mail.event.TransportEvent + */ + +public interface TransportListener extends java.util.EventListener { + + /** + * Invoked when a Message is succesfully delivered. + * @param e TransportEvent + */ + public void messageDelivered(TransportEvent e); + + /** + * Invoked when a Message is not delivered. + * @param e TransportEvent + * @see TransportEvent + */ + public void messageNotDelivered(TransportEvent e); + + /** + * Invoked when a Message is partially delivered. + * @param e TransportEvent + * @see TransportEvent + */ + public void messagePartiallyDelivered(TransportEvent e); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/event/package.html b/fine-third-default/fine-mail/src/javax/mail/event/package.html new file mode 100644 index 000000000..72c3a5935 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/event/package.html @@ -0,0 +1,55 @@ + + + + + + + + + +Listeners and events for the JavaMail API. +This package defines listener classes and event classes used by the classes +defined in the javax.mail package. + + + diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/AddressException.java b/fine-third-default/fine-mail/src/javax/mail/internet/AddressException.java new file mode 100644 index 000000000..9a2e0ef93 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/AddressException.java @@ -0,0 +1,138 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +/** + * The exception thrown when a wrongly formatted address is encountered. + * + * @author Bill Shannon + * @author Max Spivak + */ + +public class AddressException extends ParseException { + /** + * The string being parsed. + * + * @serial + */ + protected String ref = null; + + /** + * The index in the string where the error occurred, or -1 if not known. + * + * @serial + */ + protected int pos = -1; + + private static final long serialVersionUID = 9134583443539323120L; + + /** + * Constructs an AddressException with no detail message. + */ + public AddressException() { + super(); + } + + /** + * Constructs an AddressException with the specified detail message. + * @param s the detail message + */ + public AddressException(String s) { + super(s); + } + + /** + * Constructs an AddressException with the specified detail message + * and reference info. + * + * @param s the detail message + * @param ref the string being parsed + */ + public AddressException(String s, String ref) { + super(s); + this.ref = ref; + } + + /** + * Constructs an AddressException with the specified detail message + * and reference info. + * + * @param s the detail message + * @param ref the string being parsed + * @param pos the position of the error + */ + public AddressException(String s, String ref, int pos) { + super(s); + this.ref = ref; + this.pos = pos; + } + + /** + * Get the string that was being parsed when the error was detected + * (null if not relevant). + * + * @return the string that was being parsed + */ + public String getRef() { + return ref; + } + + /** + * Get the position with the reference string where the error was + * detected (-1 if not relevant). + * + * @return the position within the string of the error + */ + public int getPos() { + return pos; + } + + @Override + public String toString() { + String s = super.toString(); + if (ref == null) + return s; + s += " in string ``" + ref + "''"; + if (pos < 0) + return s; + return s + " at position " + pos; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/ContentDisposition.java b/fine-third-default/fine-mail/src/javax/mail/internet/ContentDisposition.java new file mode 100644 index 000000000..36c4aba7b --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/ContentDisposition.java @@ -0,0 +1,210 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import javax.mail.*; +import java.util.*; +import java.io.*; +import com.sun.mail.util.PropUtil; + +/** + * This class represents a MIME ContentDisposition value. It provides + * methods to parse a ContentDisposition string into individual components + * and to generate a MIME style ContentDisposition string. + * + * @author John Mani + */ + +public class ContentDisposition { + + private static final boolean contentDispositionStrict = + PropUtil.getBooleanSystemProperty("mail.mime.contentdisposition.strict", true); + + private String disposition; // disposition + private ParameterList list; // parameter list + + /** + * No-arg Constructor. + */ + public ContentDisposition() { } + + /** + * Constructor. + * + * @param disposition disposition + * @param list ParameterList + * @since JavaMail 1.2 + */ + public ContentDisposition(String disposition, ParameterList list) { + this.disposition = disposition; + this.list = list; + } + + /** + * Constructor that takes a ContentDisposition string. The String + * is parsed into its constituents: dispostion and parameters. + * A ParseException is thrown if the parse fails. + * + * @param s the ContentDisposition string. + * @exception ParseException if the parse fails. + * @since JavaMail 1.2 + */ + public ContentDisposition(String s) throws ParseException { + HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME); + HeaderTokenizer.Token tk; + + // First "disposition" .. + tk = h.next(); + if (tk.getType() != HeaderTokenizer.Token.ATOM) { + if (contentDispositionStrict) { + throw new ParseException("Expected disposition, got " + + tk.getValue()); + } + } else { + disposition = tk.getValue(); + } + + // Then parameters .. + String rem = h.getRemainder(); + if (rem != null) { + try { + list = new ParameterList(rem); + } catch (ParseException px) { + if (contentDispositionStrict) { + throw px; + } + } + } + } + + /** + * Return the disposition value. + * @return the disposition + * @since JavaMail 1.2 + */ + public String getDisposition() { + return disposition; + } + + /** + * Return the specified parameter value. Returns null + * if this parameter is absent. + * + * @param name the parameter name + * @return parameter value + * @since JavaMail 1.2 + */ + public String getParameter(String name) { + if (list == null) + return null; + + return list.get(name); + } + + /** + * Return a ParameterList object that holds all the available + * parameters. Returns null if no parameters are available. + * + * @return ParameterList + * @since JavaMail 1.2 + */ + public ParameterList getParameterList() { + return list; + } + + /** + * Set the disposition. Replaces the existing disposition. + * @param disposition the disposition + * @since JavaMail 1.2 + */ + public void setDisposition(String disposition) { + this.disposition = disposition; + } + + /** + * Set the specified parameter. If this parameter already exists, + * it is replaced by this new value. + * + * @param name parameter name + * @param value parameter value + * @since JavaMail 1.2 + */ + public void setParameter(String name, String value) { + if (list == null) + list = new ParameterList(); + + list.set(name, value); + } + + /** + * Set a new ParameterList. + * @param list ParameterList + * @since JavaMail 1.2 + */ + public void setParameterList(ParameterList list) { + this.list = list; + } + + /** + * Retrieve a RFC2045 style string representation of + * this ContentDisposition. Returns an empty string if + * the conversion failed. + * + * @return RFC2045 style string + * @since JavaMail 1.2 + */ + @Override + public String toString() { + if (disposition == null) + return ""; + + if (list == null) + return disposition; + + StringBuilder sb = new StringBuilder(disposition); + + // append the parameter list + // use the length of the string buffer + the length of + // the header name formatted as follows "Content-Disposition: " + sb.append(list.toString(sb.length() + 21)); + return sb.toString(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/ContentType.java b/fine-third-default/fine-mail/src/javax/mail/internet/ContentType.java new file mode 100644 index 000000000..c561574df --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/ContentType.java @@ -0,0 +1,298 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import javax.mail.*; +import java.util.*; +import java.io.*; + +/** + * This class represents a MIME Content-Type value. It provides + * methods to parse a Content-Type string into individual components + * and to generate a MIME style Content-Type string. + * + * @author John Mani + */ + +public class ContentType { + + private String primaryType; // primary type + private String subType; // subtype + private ParameterList list; // parameter list + + /** + * No-arg Constructor. + */ + public ContentType() { } + + /** + * Constructor. + * + * @param primaryType primary type + * @param subType subType + * @param list ParameterList + */ + public ContentType(String primaryType, String subType, + ParameterList list) { + this.primaryType = primaryType; + this.subType = subType; + this.list = list; + } + + /** + * Constructor that takes a Content-Type string. The String + * is parsed into its constituents: primaryType, subType + * and parameters. A ParseException is thrown if the parse fails. + * + * @param s the Content-Type string. + * @exception ParseException if the parse fails. + */ + public ContentType(String s) throws ParseException { + HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME); + HeaderTokenizer.Token tk; + + // First "type" .. + tk = h.next(); + if (tk.getType() != HeaderTokenizer.Token.ATOM) + throw new ParseException("In Content-Type string <" + s + ">" + + ", expected MIME type, got " + + tk.getValue()); + primaryType = tk.getValue(); + + // The '/' separator .. + tk = h.next(); + if ((char)tk.getType() != '/') + throw new ParseException("In Content-Type string <" + s + ">" + + ", expected '/', got " + tk.getValue()); + + // Then "subType" .. + tk = h.next(); + if (tk.getType() != HeaderTokenizer.Token.ATOM) + throw new ParseException("In Content-Type string <" + s + ">" + + ", expected MIME subtype, got " + + tk.getValue()); + subType = tk.getValue(); + + // Finally parameters .. + String rem = h.getRemainder(); + if (rem != null) + list = new ParameterList(rem); + } + + /** + * Return the primary type. + * @return the primary type + */ + public String getPrimaryType() { + return primaryType; + } + + /** + * Return the subType. + * @return the subType + */ + public String getSubType() { + return subType; + } + + /** + * Return the MIME type string, without the parameters. + * The returned value is basically the concatenation of + * the primaryType, the '/' character and the secondaryType. + * + * @return the type + */ + public String getBaseType() { + if (primaryType == null || subType == null) + return ""; + return primaryType + '/' + subType; + } + + /** + * Return the specified parameter value. Returns null + * if this parameter is absent. + * + * @param name the parameter name + * @return parameter value + */ + public String getParameter(String name) { + if (list == null) + return null; + + return list.get(name); + } + + /** + * Return a ParameterList object that holds all the available + * parameters. Returns null if no parameters are available. + * + * @return ParameterList + */ + public ParameterList getParameterList() { + return list; + } + + /** + * Set the primary type. Overrides existing primary type. + * @param primaryType primary type + */ + public void setPrimaryType(String primaryType) { + this.primaryType = primaryType; + } + + /** + * Set the subType. Replaces the existing subType. + * @param subType the subType + */ + public void setSubType(String subType) { + this.subType = subType; + } + + /** + * Set the specified parameter. If this parameter already exists, + * it is replaced by this new value. + * + * @param name parameter name + * @param value parameter value + */ + public void setParameter(String name, String value) { + if (list == null) + list = new ParameterList(); + + list.set(name, value); + } + + /** + * Set a new ParameterList. + * @param list ParameterList + */ + public void setParameterList(ParameterList list) { + this.list = list; + } + + /** + * Retrieve a RFC2045 style string representation of + * this Content-Type. Returns an empty string if + * the conversion failed. + * + * @return RFC2045 style string + */ + @Override + public String toString() { + if (primaryType == null || subType == null) // need both + return ""; + + StringBuilder sb = new StringBuilder(); + sb.append(primaryType).append('/').append(subType); + if (list != null) + // append the parameter list + // use the length of the string buffer + the length of + // the header name formatted as follows "Content-Type: " + sb.append(list.toString(sb.length() + 14)); + + return sb.toString(); + } + + /** + * Match with the specified ContentType object. This method + * compares only the primaryType and + * subType . The parameters of both operands + * are ignored.

    + * + * For example, this method will return true when + * comparing the ContentTypes for "text/plain" + * and "text/plain; charset=foobar". + * + * If the subType of either operand is the special + * character '*', then the subtype is ignored during the match. + * For example, this method will return true when + * comparing the ContentTypes for "text/plain" + * and "text/*" + * + * @param cType ContentType to compare this against + * @return true if it matches + */ + public boolean match(ContentType cType) { + // Match primaryType + if (!((primaryType == null && cType.getPrimaryType() == null) || + (primaryType != null && + primaryType.equalsIgnoreCase(cType.getPrimaryType())))) + return false; + + String sType = cType.getSubType(); + + // If either one of the subTypes is wildcarded, return true + if ((subType != null && subType.startsWith("*")) || + (sType != null && sType.startsWith("*"))) + return true; + + // Match subType + return (subType == null && sType == null) || + (subType != null && subType.equalsIgnoreCase(sType)); + } + + /** + * Match with the specified content-type string. This method + * compares only the primaryType and + * subType . + * The parameters of both operands are ignored.

    + * + * For example, this method will return true when + * comparing the ContentType for "text/plain" + * with "text/plain; charset=foobar". + * + * If the subType of either operand is the special + * character '*', then the subtype is ignored during the match. + * For example, this method will return true when + * comparing the ContentType for "text/plain" + * with "text/*" + * + * @param s the content-type string to match + * @return true if it matches + */ + public boolean match(String s) { + try { + return match(new ContentType(s)); + } catch (ParseException pex) { + return false; + } + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/HeaderTokenizer.java b/fine-third-default/fine-mail/src/javax/mail/internet/HeaderTokenizer.java new file mode 100644 index 000000000..c6946449f --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/HeaderTokenizer.java @@ -0,0 +1,495 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import java.util.*; + +/** + * This class tokenizes RFC822 and MIME headers into the basic + * symbols specified by RFC822 and MIME.

    + * + * This class handles folded headers (ie headers with embedded + * CRLF SPACE sequences). The folds are removed in the returned + * tokens. + * + * @author John Mani + * @author Bill Shannon + */ + +public class HeaderTokenizer { + + /** + * The Token class represents tokens returned by the + * HeaderTokenizer. + */ + public static class Token { + + private int type; + private String value; + + /** + * Token type indicating an ATOM. + */ + public static final int ATOM = -1; + + /** + * Token type indicating a quoted string. The value + * field contains the string without the quotes. + */ + public static final int QUOTEDSTRING = -2; + + /** + * Token type indicating a comment. The value field + * contains the comment string without the comment + * start and end symbols. + */ + public static final int COMMENT = -3; + + /** + * Token type indicating end of input. + */ + public static final int EOF = -4; + + /** + * Constructor. + * @param type Token type + * @param value Token value + */ + public Token(int type, String value) { + this.type = type; + this.value = value; + } + + /** + * Return the type of the token. If the token represents a + * delimiter or a control character, the type is that character + * itself, converted to an integer. Otherwise, it's value is + * one of the following: + *

      + *
    • ATOM A sequence of ASCII characters + * delimited by either SPACE, CTL, "(", <"> or the + * specified SPECIALS + *
    • QUOTEDSTRING A sequence of ASCII characters + * within quotes + *
    • COMMENT A sequence of ASCII characters + * within "(" and ")". + *
    • EOF End of header + *
    + * + * @return the token type + */ + public int getType() { + return type; + } + + /** + * Returns the value of the token just read. When the current + * token is a quoted string, this field contains the body of the + * string, without the quotes. When the current token is a comment, + * this field contains the body of the comment. + * + * @return token value + */ + public String getValue() { + return value; + } + } + + private String string; // the string to be tokenized + private boolean skipComments; // should comments be skipped ? + private String delimiters; // delimiter string + private int currentPos; // current parse position + private int maxPos; // string length + private int nextPos; // track start of next Token for next() + private int peekPos; // track start of next Token for peek() + + /** + * RFC822 specials + */ + public final static String RFC822 = "()<>@,;:\\\"\t .[]"; + + /** + * MIME specials + */ + public final static String MIME = "()<>@,;:\\\"\t []/?="; + + // The EOF Token + private final static Token EOFToken = new Token(Token.EOF, null); + + /** + * Constructor that takes a rfc822 style header. + * + * @param header The rfc822 header to be tokenized + * @param delimiters Set of delimiter characters + * to be used to delimit ATOMS. These + * are usually RFC822 or + * MIME + * @param skipComments If true, comments are skipped and + * not returned as tokens + */ + public HeaderTokenizer(String header, String delimiters, + boolean skipComments) { + string = (header == null) ? "" : header; // paranoia ?! + this.skipComments = skipComments; + this.delimiters = delimiters; + currentPos = nextPos = peekPos = 0; + maxPos = string.length(); + } + + /** + * Constructor. Comments are ignored and not returned as tokens + * + * @param header The header that is tokenized + * @param delimiters The delimiters to be used + */ + public HeaderTokenizer(String header, String delimiters) { + this(header, delimiters, true); + } + + /** + * Constructor. The RFC822 defined delimiters - RFC822 - are + * used to delimit ATOMS. Also comments are skipped and not + * returned as tokens + * + * @param header the header string + */ + public HeaderTokenizer(String header) { + this(header, RFC822); + } + + /** + * Parses the next token from this String.

    + * + * Clients sit in a loop calling next() to parse successive + * tokens until an EOF Token is returned. + * + * @return the next Token + * @exception ParseException if the parse fails + */ + public Token next() throws ParseException { + return next('\0', false); + } + + /** + * Parses the next token from this String. + * If endOfAtom is not NUL, the token extends until the + * endOfAtom character is seen, or to the end of the header. + * This method is useful when parsing headers that don't + * obey the MIME specification, e.g., by failing to quote + * parameter values that contain spaces. + * + * @param endOfAtom if not NUL, character marking end of token + * @return the next Token + * @exception ParseException if the parse fails + * @since JavaMail 1.5 + */ + public Token next(char endOfAtom) throws ParseException { + return next(endOfAtom, false); + } + + /** + * Parses the next token from this String. + * endOfAtom is handled as above. If keepEscapes is true, + * any backslash escapes are preserved in the returned string. + * This method is useful when parsing headers that don't + * obey the MIME specification, e.g., by failing to escape + * backslashes in the filename parameter. + * + * @param endOfAtom if not NUL, character marking end of token + * @param keepEscapes keep all backslashes in returned string? + * @return the next Token + * @exception ParseException if the parse fails + * @since JavaMail 1.5 + */ + public Token next(char endOfAtom, boolean keepEscapes) + throws ParseException { + Token tk; + + currentPos = nextPos; // setup currentPos + tk = getNext(endOfAtom, keepEscapes); + nextPos = peekPos = currentPos; // update currentPos and peekPos + return tk; + } + + /** + * Peek at the next token, without actually removing the token + * from the parse stream. Invoking this method multiple times + * will return successive tokens, until next() is + * called.

    + * + * @return the next Token + * @exception ParseException if the parse fails + */ + public Token peek() throws ParseException { + Token tk; + + currentPos = peekPos; // setup currentPos + tk = getNext('\0', false); + peekPos = currentPos; // update peekPos + return tk; + } + + /** + * Return the rest of the Header. + * + * @return String rest of header. null is returned if we are + * already at end of header + */ + public String getRemainder() { + if (nextPos >= string.length()) + return null; + return string.substring(nextPos); + } + + /* + * Return the next token starting from 'currentPos'. After the + * parse, 'currentPos' is updated to point to the start of the + * next token. + */ + private Token getNext(char endOfAtom, boolean keepEscapes) + throws ParseException { + // If we're already at end of string, return EOF + if (currentPos >= maxPos) + return EOFToken; + + // Skip white-space, position currentPos beyond the space + if (skipWhiteSpace() == Token.EOF) + return EOFToken; + + char c; + int start; + boolean filter = false; + + c = string.charAt(currentPos); + + // Check or Skip comments and position currentPos + // beyond the comment + while (c == '(') { + // Parsing comment .. + int nesting; + for (start = ++currentPos, nesting = 1; + nesting > 0 && currentPos < maxPos; + currentPos++) { + c = string.charAt(currentPos); + if (c == '\\') { // Escape sequence + currentPos++; // skip the escaped character + filter = true; + } else if (c == '\r') + filter = true; + else if (c == '(') + nesting++; + else if (c == ')') + nesting--; + } + if (nesting != 0) + throw new ParseException("Unbalanced comments"); + + if (!skipComments) { + // Return the comment, if we are asked to. + // Note that the comment start & end markers are ignored. + String s; + if (filter) // need to go thru the token again. + s = filterToken(string, start, currentPos-1, keepEscapes); + else + s = string.substring(start,currentPos-1); + + return new Token(Token.COMMENT, s); + } + + // Skip any whitespace after the comment. + if (skipWhiteSpace() == Token.EOF) + return EOFToken; + c = string.charAt(currentPos); + } + + // Check for quoted-string and position currentPos + // beyond the terminating quote + if (c == '"') { + currentPos++; // skip initial quote + return collectString('"', keepEscapes); + } + + // Check for SPECIAL or CTL + if (c < 040 || c >= 0177 || delimiters.indexOf(c) >= 0) { + if (endOfAtom > 0 && c != endOfAtom) { + // not expecting a special character here, + // pretend it's a quoted string + return collectString(endOfAtom, keepEscapes); + } + currentPos++; // re-position currentPos + char ch[] = new char[1]; + ch[0] = c; + return new Token((int)c, new String(ch)); + } + + // Check for ATOM + for (start = currentPos; currentPos < maxPos; currentPos++) { + c = string.charAt(currentPos); + // ATOM is delimited by either SPACE, CTL, "(", <"> + // or the specified SPECIALS + if (c < 040 || c >= 0177 || c == '(' || c == ' ' || + c == '"' || delimiters.indexOf(c) >= 0) { + if (endOfAtom > 0 && c != endOfAtom) { + // not the expected atom after all; + // back up and pretend it's a quoted string + currentPos = start; + return collectString(endOfAtom, keepEscapes); + } + break; + } + } + return new Token(Token.ATOM, string.substring(start, currentPos)); + } + + private Token collectString(char eos, boolean keepEscapes) + throws ParseException { + int start; + boolean filter = false; + for (start = currentPos; currentPos < maxPos; currentPos++) { + char c = string.charAt(currentPos); + if (c == '\\') { // Escape sequence + currentPos++; + filter = true; + } else if (c == '\r') + filter = true; + else if (c == eos) { + currentPos++; + String s; + + if (filter) + s = filterToken(string, start, currentPos-1, keepEscapes); + else + s = string.substring(start, currentPos-1); + + if (c != '"') { // not a real quoted string + s = trimWhiteSpace(s); + currentPos--; // back up before the eos char + } + + return new Token(Token.QUOTEDSTRING, s); + } + } + + // ran off the end of the string + + // if we're looking for a matching quote, that's an error + if (eos == '"') + throw new ParseException("Unbalanced quoted string"); + + // otherwise, just return whatever's left + String s; + if (filter) + s = filterToken(string, start, currentPos, keepEscapes); + else + s = string.substring(start, currentPos); + s = trimWhiteSpace(s); + return new Token(Token.QUOTEDSTRING, s); + } + + // Skip SPACE, HT, CR and NL + private int skipWhiteSpace() { + char c; + for (; currentPos < maxPos; currentPos++) + if (((c = string.charAt(currentPos)) != ' ') && + (c != '\t') && (c != '\r') && (c != '\n')) + return currentPos; + return Token.EOF; + } + + // Trim SPACE, HT, CR and NL from end of string + private static String trimWhiteSpace(String s) { + char c; + int i; + for (i = s.length() - 1; i >= 0; i--) { + if (((c = s.charAt(i)) != ' ') && + (c != '\t') && (c != '\r') && (c != '\n')) + break; + } + if (i <= 0) + return ""; + else + return s.substring(0, i + 1); + } + + /* Process escape sequences and embedded LWSPs from a comment or + * quoted string. + */ + private static String filterToken(String s, int start, int end, + boolean keepEscapes) { + StringBuilder sb = new StringBuilder(); + char c; + boolean gotEscape = false; + boolean gotCR = false; + + for (int i = start; i < end; i++) { + c = s.charAt(i); + if (c == '\n' && gotCR) { + // This LF is part of an unescaped + // CRLF sequence (i.e, LWSP). Skip it. + gotCR = false; + continue; + } + + gotCR = false; + if (!gotEscape) { + // Previous character was NOT '\' + if (c == '\\') // skip this character + gotEscape = true; + else if (c == '\r') // skip this character + gotCR = true; + else // append this character + sb.append(c); + } else { + // Previous character was '\'. So no need to + // bother with any special processing, just + // append this character. If keepEscapes is + // set, keep the backslash. IE6 fails to escape + // backslashes in quoted strings in HTTP headers, + // e.g., in the filename parameter. + if (keepEscapes) + sb.append('\\'); + sb.append(c); + gotEscape = false; + } + } + return sb.toString(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/InternetAddress.java b/fine-third-default/fine-mail/src/javax/mail/internet/InternetAddress.java new file mode 100644 index 000000000..59ab969f9 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/InternetAddress.java @@ -0,0 +1,1547 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; +import java.util.ArrayList; +import java.util.StringTokenizer; +import java.util.Locale; +import java.nio.charset.StandardCharsets; +import javax.mail.*; +import com.sun.mail.util.PropUtil; + +/** + * This class represents an Internet email address using the syntax + * of RFC822. + * Typical address syntax is of the form "user@host.domain" or + * "Personal Name <user@host.domain>". + * + * @author Bill Shannon + * @author John Mani + */ + +public class InternetAddress extends Address implements Cloneable { + + protected String address; // email address + + /** + * The personal name. + */ + protected String personal; + + /** + * The RFC 2047 encoded version of the personal name.

    + * + * This field and the personal field track each + * other, so if a subclass sets one of these fields directly, it + * should set the other to null, so that it is + * suitably recomputed. + */ + protected String encodedPersonal; + + private static final long serialVersionUID = -7507595530758302903L; + + private static final boolean ignoreBogusGroupName = + PropUtil.getBooleanSystemProperty( + "mail.mime.address.ignorebogusgroupname", true); + + private static final boolean useCanonicalHostName = + PropUtil.getBooleanSystemProperty( + "mail.mime.address.usecanonicalhostname", true); + + private static final boolean allowUtf8 = + PropUtil.getBooleanSystemProperty("mail.mime.allowutf8", false); + + /** + * Default constructor. + */ + public InternetAddress() { } + + /** + * Constructor.

    + * + * Parse the given string and create an InternetAddress. + * See the parse method for details of the parsing. + * The address is parsed using "strict" parsing. + * This constructor does not perform the additional + * syntax checks that the + * InternetAddress(String address, boolean strict) + * constructor does when strict is true. + * This constructor is equivalent to + * InternetAddress(address, false). + * + * @param address the address in RFC822 format + * @exception AddressException if the parse failed + */ + public InternetAddress(String address) throws AddressException { + // use our address parsing utility routine to parse the string + InternetAddress a[] = parse(address, true); + // if we got back anything other than a single address, it's an error + if (a.length != 1) + throw new AddressException("Illegal address", address); + + /* + * Now copy the contents of the single address we parsed + * into the current object, which will be returned from the + * constructor. + * XXX - this sure is a round-about way of getting this done. + */ + this.address = a[0].address; + this.personal = a[0].personal; + this.encodedPersonal = a[0].encodedPersonal; + } + + /** + * Parse the given string and create an InternetAddress. + * If strict is false, the detailed syntax of the + * address isn't checked. + * + * @param address the address in RFC822 format + * @param strict enforce RFC822 syntax + * @exception AddressException if the parse failed + * @since JavaMail 1.3 + */ + public InternetAddress(String address, boolean strict) + throws AddressException { + this(address); + if (strict) { + if (isGroup()) + getGroup(true); // throw away the result + else + checkAddress(this.address, true, true); + } + } + + /** + * Construct an InternetAddress given the address and personal name. + * The address is assumed to be a syntactically valid RFC822 address. + * + * @param address the address in RFC822 format + * @param personal the personal name + * @exception UnsupportedEncodingException if the personal name + * can't be encoded in the given charset + */ + public InternetAddress(String address, String personal) + throws UnsupportedEncodingException { + this(address, personal, null); + } + + /** + * Construct an InternetAddress given the address and personal name. + * The address is assumed to be a syntactically valid RFC822 address. + * + * @param address the address in RFC822 format + * @param personal the personal name + * @param charset the MIME charset for the name + * @exception UnsupportedEncodingException if the personal name + * can't be encoded in the given charset + */ + public InternetAddress(String address, String personal, String charset) + throws UnsupportedEncodingException { + this.address = address; + setPersonal(personal, charset); + } + + /** + * Return a copy of this InternetAddress object. + * @since JavaMail 1.2 + */ + @Override + public Object clone() { + InternetAddress a = null; + try { + a = (InternetAddress)super.clone(); + } catch (CloneNotSupportedException e) {} // Won't happen + return a; + } + + /** + * Return the type of this address. The type of an InternetAddress + * is "rfc822". + */ + @Override + public String getType() { + return "rfc822"; + } + + /** + * Set the email address. + * + * @param address email address + */ + public void setAddress(String address) { + this.address = address; + } + + /** + * Set the personal name. If the name contains non US-ASCII + * characters, then the name will be encoded using the specified + * charset as per RFC 2047. If the name contains only US-ASCII + * characters, no encoding is done and the name is used as is.

    + * + * @param name personal name + * @param charset MIME charset to be used to encode the name as + * per RFC 2047 + * @see #setPersonal(String) + * @exception UnsupportedEncodingException if the charset encoding + * fails. + */ + public void setPersonal(String name, String charset) + throws UnsupportedEncodingException { + personal = name; + if (name != null) + encodedPersonal = MimeUtility.encodeWord(name, charset, null); + else + encodedPersonal = null; + } + + /** + * Set the personal name. If the name contains non US-ASCII + * characters, then the name will be encoded using the platform's + * default charset. If the name contains only US-ASCII characters, + * no encoding is done and the name is used as is.

    + * + * @param name personal name + * @see #setPersonal(String name, String charset) + * @exception UnsupportedEncodingException if the charset encoding + * fails. + */ + public void setPersonal(String name) + throws UnsupportedEncodingException { + personal = name; + if (name != null) + encodedPersonal = MimeUtility.encodeWord(name); + else + encodedPersonal = null; + } + + /** + * Get the email address. + * @return email address + */ + public String getAddress() { + return address; + } + + /** + * Get the personal name. If the name is encoded as per RFC 2047, + * it is decoded and converted into Unicode. If the decoding or + * conversion fails, the raw data is returned as is. + * + * @return personal name + */ + public String getPersonal() { + if (personal != null) + return personal; + + if (encodedPersonal != null) { + try { + personal = MimeUtility.decodeText(encodedPersonal); + return personal; + } catch (Exception ex) { + // 1. ParseException: either its an unencoded string or + // it can't be parsed + // 2. UnsupportedEncodingException: can't decode it. + return encodedPersonal; + } + } + // No personal or encodedPersonal, return null + return null; + } + + /** + * Convert this address into a RFC 822 / RFC 2047 encoded address. + * The resulting string contains only US-ASCII characters, and + * hence is mail-safe. + * + * @return possibly encoded address string + */ + @Override + public String toString() { + String a = address == null ? "" : address; + if (encodedPersonal == null && personal != null) + try { + encodedPersonal = MimeUtility.encodeWord(personal); + } catch (UnsupportedEncodingException ex) { } + + if (encodedPersonal != null) + return quotePhrase(encodedPersonal) + " <" + a + ">"; + else if (isGroup() || isSimple()) + return a; + else + return "<" + a + ">"; + } + + /** + * Returns a properly formatted address (RFC 822 syntax) of + * Unicode characters. + * + * @return Unicode address string + * @since JavaMail 1.2 + */ + public String toUnicodeString() { + String p = getPersonal(); + if (p != null) + return quotePhrase(p) + " <" + address + ">"; + else if (isGroup() || isSimple()) + return address; + else + return "<" + address + ">"; + } + + /* + * quotePhrase() quotes the words within a RFC822 phrase. + * + * This is tricky, since a phrase is defined as 1 or more + * RFC822 words, separated by LWSP. Now, a word that contains + * LWSP is supposed to be quoted, and this is exactly what the + * MimeUtility.quote() method does. However, when dealing with + * a phrase, any LWSP encountered can be construed to be the + * separator between words, and not part of the words themselves. + * To deal with this funkiness, we have the below variant of + * MimeUtility.quote(), which essentially ignores LWSP when + * deciding whether to quote a word. + * + * It aint pretty, but it gets the job done :) + */ + + private static final String rfc822phrase = + HeaderTokenizer.RFC822.replace(' ', '\0').replace('\t', '\0'); + + private static String quotePhrase(String phrase) { + int len = phrase.length(); + boolean needQuoting = false; + + for (int i = 0; i < len; i++) { + char c = phrase.charAt(i); + if (c == '"' || c == '\\') { + // need to escape them and then quote the whole string + StringBuilder sb = new StringBuilder(len + 3); + sb.append('"'); + for (int j = 0; j < len; j++) { + char cc = phrase.charAt(j); + if (cc == '"' || cc == '\\') + // Escape the character + sb.append('\\'); + sb.append(cc); + } + sb.append('"'); + return sb.toString(); + } else if ((c < 040 && c != '\r' && c != '\n' && c != '\t') || + (c >= 0177 && !allowUtf8) || rfc822phrase.indexOf(c) >= 0) + // These characters cause the string to be quoted + needQuoting = true; + } + + if (needQuoting) { + StringBuilder sb = new StringBuilder(len + 2); + sb.append('"').append(phrase).append('"'); + return sb.toString(); + } else + return phrase; + } + + private static String unquote(String s) { + if (s.startsWith("\"") && s.endsWith("\"") && s.length() > 1) { + s = s.substring(1, s.length() - 1); + // check for any escaped characters + if (s.indexOf('\\') >= 0) { + StringBuilder sb = new StringBuilder(s.length()); // approx + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\\' && i < s.length() - 1) + c = s.charAt(++i); + sb.append(c); + } + s = sb.toString(); + } + } + return s; + } + + /** + * The equality operator. + */ + @Override + public boolean equals(Object a) { + if (!(a instanceof InternetAddress)) + return false; + + String s = ((InternetAddress)a).getAddress(); + if (s == address) + return true; + if (address != null && address.equalsIgnoreCase(s)) + return true; + + return false; + } + + /** + * Compute a hash code for the address. + */ + @Override + public int hashCode() { + if (address == null) + return 0; + else + return address.toLowerCase(Locale.ENGLISH).hashCode(); + } + + /** + * Convert the given array of InternetAddress objects into + * a comma separated sequence of address strings. The + * resulting string contains only US-ASCII characters, and + * hence is mail-safe.

    + * + * @param addresses array of InternetAddress objects + * @exception ClassCastException if any address object in the + * given array is not an InternetAddress object. Note + * that this is a RuntimeException. + * @return comma separated string of addresses + */ + public static String toString(Address[] addresses) { + return toString(addresses, 0); + } + + /** + * Convert the given array of InternetAddress objects into + * a comma separated sequence of address strings. The + * resulting string contains Unicode characters.

    + * + * @param addresses array of InternetAddress objects + * @exception ClassCastException if any address object in the + * given array is not an InternetAddress object. Note + * that this is a RuntimeException. + * @return comma separated string of addresses + * @since JavaMail 1.6 + */ + public static String toUnicodeString(Address[] addresses) { + return toUnicodeString(addresses, 0); + } + + /** + * Convert the given array of InternetAddress objects into + * a comma separated sequence of address strings. The + * resulting string contains only US-ASCII characters, and + * hence is mail-safe.

    + * + * The 'used' parameter specifies the number of character positions + * already taken up in the field into which the resulting address + * sequence string is to be inserted. It is used to determine the + * line-break positions in the resulting address sequence string. + * + * @param addresses array of InternetAddress objects + * @param used number of character positions already used, in + * the field into which the address string is to + * be inserted. + * @exception ClassCastException if any address object in the + * given array is not an InternetAddress object. Note + * that this is a RuntimeException. + * @return comma separated string of addresses + */ + public static String toString(Address[] addresses, int used) { + if (addresses == null || addresses.length == 0) + return null; + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < addresses.length; i++) { + if (i != 0) { // need to append comma + sb.append(", "); + used += 2; + } + + // prefer not to split a single address across lines so used=0 below + String s = MimeUtility.fold(0, addresses[i].toString()); + int len = lengthOfFirstSegment(s); // length till CRLF + if (used + len > 76) { // overflows ... + // smash trailing space from ", " above + int curlen = sb.length(); + if (curlen > 0 && sb.charAt(curlen - 1) == ' ') + sb.setLength(curlen - 1); + sb.append("\r\n\t"); // .. start new continuation line + used = 8; // account for the starting char + } + sb.append(s); + used = lengthOfLastSegment(s, used); + } + + return sb.toString(); + } + + /** + * Convert the given array of InternetAddress objects into + * a comma separated sequence of address strings. The + * resulting string contains Unicode characters.

    + * + * The 'used' parameter specifies the number of character positions + * already taken up in the field into which the resulting address + * sequence string is to be inserted. It is used to determine the + * line-break positions in the resulting address sequence string. + * + * @param addresses array of InternetAddress objects + * @param used number of character positions already used, in + * the field into which the address string is to + * be inserted. + * @exception ClassCastException if any address object in the + * given array is not an InternetAddress object. Note + * that this is a RuntimeException. + * @return comma separated string of addresses + * @since JavaMail 1.6 + */ + /* + * XXX - This is exactly the same as the above, except it uses + * toUnicodeString instead of toString. + * XXX - Since the line length restrictions are in bytes, not characters, + * we convert all non-ASCII addresses to UTF-8 byte strings, + * which we then convert to ISO-8859-1 Strings where every + * character respresents one UTF-8 byte. At the end we reverse + * the conversion to get back to a correct Unicode string. + * This is a hack to allow all the other character-based methods + * to work properly with UTF-8 bytes. + */ + public static String toUnicodeString(Address[] addresses, int used) { + if (addresses == null || addresses.length == 0) + return null; + + StringBuilder sb = new StringBuilder(); + + boolean sawNonAscii = false; + for (int i = 0; i < addresses.length; i++) { + if (i != 0) { // need to append comma + sb.append(", "); + used += 2; + } + + // prefer not to split a single address across lines so used=0 below + String as = ((InternetAddress)addresses[i]).toUnicodeString(); + if (MimeUtility.checkAscii(as) != MimeUtility.ALL_ASCII) { + sawNonAscii = true; + as = new String(as.getBytes(StandardCharsets.UTF_8), + StandardCharsets.ISO_8859_1); + } + String s = MimeUtility.fold(0, as); + int len = lengthOfFirstSegment(s); // length till CRLF + if (used + len > 76) { // overflows ... + // smash trailing space from ", " above + int curlen = sb.length(); + if (curlen > 0 && sb.charAt(curlen - 1) == ' ') + sb.setLength(curlen - 1); + sb.append("\r\n\t"); // .. start new continuation line + used = 8; // account for the starting char + } + sb.append(s); + used = lengthOfLastSegment(s, used); + } + + String ret = sb.toString(); + if (sawNonAscii) + ret = new String(ret.getBytes(StandardCharsets.ISO_8859_1), + StandardCharsets.UTF_8); + return ret; + } + + /* + * Return the length of the first segment within this string. + * If no segments exist, the length of the whole line is returned. + */ + private static int lengthOfFirstSegment(String s) { + int pos; + if ((pos = s.indexOf("\r\n")) != -1) + return pos; + else + return s.length(); + } + + /* + * Return the length of the last segment within this string. + * If no segments exist, the length of the whole line plus + * used is returned. + */ + private static int lengthOfLastSegment(String s, int used) { + int pos; + if ((pos = s.lastIndexOf("\r\n")) != -1) + return s.length() - pos - 2; + else + return s.length() + used; + } + + /** + * Return an InternetAddress object representing the current user. + * The entire email address may be specified in the "mail.from" + * property. If not set, the "mail.user" and "mail.host" properties + * are tried. If those are not set, the "user.name" property and + * InetAddress.getLocalHost method are tried. + * Security exceptions that may occur while accessing this information + * are ignored. If it is not possible to determine an email address, + * null is returned. + * + * @param session Session object used for property lookup + * @return current user's email address + */ + public static InternetAddress getLocalAddress(Session session) { + try { + return _getLocalAddress(session); + } catch (SecurityException sex) { // ignore it + } catch (AddressException ex) { // ignore it + } catch (UnknownHostException ex) { } // ignore it + return null; + } + + /** + * A package-private version of getLocalAddress that doesn't swallow + * the exception. Used by MimeMessage.setFrom() to report the reason + * for the failure. + */ + // package-private + static InternetAddress _getLocalAddress(Session session) + throws SecurityException, AddressException, UnknownHostException { + String user = null, host = null, address = null; + if (session == null) { + user = System.getProperty("user.name"); + host = getLocalHostName(); + } else { + address = session.getProperty("mail.from"); + if (address == null) { + user = session.getProperty("mail.user"); + if (user == null || user.length() == 0) + user = session.getProperty("user.name"); + if (user == null || user.length() == 0) + user = System.getProperty("user.name"); + host = session.getProperty("mail.host"); + if (host == null || host.length() == 0) + host = getLocalHostName(); + } + } + + if (address == null && user != null && user.length() != 0 && + host != null && host.length() != 0) + address = MimeUtility.quote(user.trim(), specialsNoDot + "\t ") + + "@" + host; + + if (address == null) + return null; + + return new InternetAddress(address); + } + + /** + * Get the local host name from InetAddress and return it in a form + * suitable for use in an email address. + */ + private static String getLocalHostName() throws UnknownHostException { + String host = null; + InetAddress me = InetAddress.getLocalHost(); + if (me != null) { + // try canonical host name first + if (useCanonicalHostName) + host = me.getCanonicalHostName(); + if (host == null) + host = me.getHostName(); + // if we can't get our name, use local address literal + if (host == null) + host = me.getHostAddress(); + if (host != null && host.length() > 0 && isInetAddressLiteral(host)) + host = '[' + host + ']'; + } + return host; + } + + /** + * Is the address an IPv4 or IPv6 address literal, which needs to + * be enclosed in "[]" in an email address? IPv4 literals contain + * decimal digits and dots, IPv6 literals contain hex digits, dots, + * and colons. We're lazy and don't check the exact syntax, just + * the allowed characters; strings that have only the allowed + * characters in a literal but don't meet the syntax requirements + * for a literal definitely can't be a host name and thus will fail + * later when used as an address literal. + */ + private static boolean isInetAddressLiteral(String addr) { + boolean sawHex = false, sawColon = false; + for (int i = 0; i < addr.length(); i++) { + char c = addr.charAt(i); + if (c >= '0' && c <= '9') + ; // digits always ok + else if (c == '.') + ; // dot always ok + else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) + sawHex = true; // need to see a colon too + else if (c == ':') + sawColon = true; + else + return false; // anything else, definitely not a literal + } + return !sawHex || sawColon; + } + + /** + * Parse the given comma separated sequence of addresses into + * InternetAddress objects. Addresses must follow RFC822 syntax. + * + * @param addresslist comma separated address strings + * @return array of InternetAddress objects + * @exception AddressException if the parse failed + */ + public static InternetAddress[] parse(String addresslist) + throws AddressException { + return parse(addresslist, true); + } + + /** + * Parse the given sequence of addresses into InternetAddress + * objects. If strict is false, simple email addresses + * separated by spaces are also allowed. If strict is + * true, many (but not all) of the RFC822 syntax rules are enforced. + * In particular, even if strict is true, addresses + * composed of simple names (with no "@domain" part) are allowed. + * Such "illegal" addresses are not uncommon in real messages.

    + * + * Non-strict parsing is typically used when parsing a list of + * mail addresses entered by a human. Strict parsing is typically + * used when parsing address headers in mail messages. + * + * @param addresslist comma separated address strings + * @param strict enforce RFC822 syntax + * @return array of InternetAddress objects + * @exception AddressException if the parse failed + */ + public static InternetAddress[] parse(String addresslist, boolean strict) + throws AddressException { + return parse(addresslist, strict, false); + } + + /** + * Parse the given sequence of addresses into InternetAddress + * objects. If strict is false, the full syntax rules for + * individual addresses are not enforced. If strict is + * true, many (but not all) of the RFC822 syntax rules are enforced.

    + * + * To better support the range of "invalid" addresses seen in real + * messages, this method enforces fewer syntax rules than the + * parse method when the strict flag is false + * and enforces more rules when the strict flag is true. If the + * strict flag is false and the parse is successful in separating out an + * email address or addresses, the syntax of the addresses themselves + * is not checked. + * + * @param addresslist comma separated address strings + * @param strict enforce RFC822 syntax + * @return array of InternetAddress objects + * @exception AddressException if the parse failed + * @since JavaMail 1.3 + */ + public static InternetAddress[] parseHeader(String addresslist, + boolean strict) throws AddressException { + return parse(MimeUtility.unfold(addresslist), strict, true); + } + + /* + * RFC822 Address parser. + * + * XXX - This is complex enough that it ought to be a real parser, + * not this ad-hoc mess, and because of that, this is not perfect. + * + * XXX - Deal with encoded Headers too. + */ + @SuppressWarnings("fallthrough") + private static InternetAddress[] parse(String s, boolean strict, + boolean parseHdr) throws AddressException { + int start, end, index, nesting; + int start_personal = -1, end_personal = -1; + int length = s.length(); + boolean ignoreErrors = parseHdr && !strict; + boolean in_group = false; // we're processing a group term + boolean route_addr = false; // address came from route-addr term + boolean rfc822 = false; // looks like an RFC822 address + char c; + List v = new ArrayList<>(); + InternetAddress ma; + + for (start = end = -1, index = 0; index < length; index++) { + c = s.charAt(index); + + switch (c) { + case '(': // We are parsing a Comment. Ignore everything inside. + // XXX - comment fields should be parsed as whitespace, + // more than one allowed per address + rfc822 = true; + if (start >= 0 && end == -1) + end = index; + int pindex = index; + for (index++, nesting = 1; index < length && nesting > 0; + index++) { + c = s.charAt(index); + switch (c) { + case '\\': + index++; // skip both '\' and the escaped char + break; + case '(': + nesting++; + break; + case ')': + nesting--; + break; + default: + break; + } + } + if (nesting > 0) { + if (!ignoreErrors) + throw new AddressException("Missing ')'", s, index); + // pretend the first paren was a regular character and + // continue parsing after it + index = pindex + 1; + break; + } + index--; // point to closing paren + if (start_personal == -1) + start_personal = pindex + 1; + if (end_personal == -1) + end_personal = index; + break; + + case ')': + if (!ignoreErrors) + throw new AddressException("Missing '('", s, index); + // pretend the left paren was a regular character and + // continue parsing + if (start == -1) + start = index; + break; + + case '<': + rfc822 = true; + if (route_addr) { + if (!ignoreErrors) + throw new AddressException( + "Extra route-addr", s, index); + + // assume missing comma between addresses + if (start == -1) { + route_addr = false; + rfc822 = false; + start = end = -1; + break; // nope, nothing there + } + if (!in_group) { + // got a token, add this to our InternetAddress list + if (end == -1) // should never happen + end = index; + String addr = s.substring(start, end).trim(); + + ma = new InternetAddress(); + ma.setAddress(addr); + if (start_personal >= 0) { + ma.encodedPersonal = unquote( + s.substring(start_personal, end_personal). + trim()); + } + v.add(ma); + + route_addr = false; + rfc822 = false; + start = end = -1; + start_personal = end_personal = -1; + // continue processing this new address... + } + } + + int rindex = index; + boolean inquote = false; + outf: + for (index++; index < length; index++) { + c = s.charAt(index); + switch (c) { + case '\\': // XXX - is this needed? + index++; // skip both '\' and the escaped char + break; + case '"': + inquote = !inquote; + break; + case '>': + if (inquote) + continue; + break outf; // out of for loop + default: + break; + } + } + + // did we find a matching quote? + if (inquote) { + if (!ignoreErrors) + throw new AddressException("Missing '\"'", s, index); + // didn't find matching quote, try again ignoring quotes + // (e.g., ``<"@foo.com>'') + outq: + for (index = rindex + 1; index < length; index++) { + c = s.charAt(index); + if (c == '\\') // XXX - is this needed? + index++; // skip both '\' and the escaped char + else if (c == '>') + break; + } + } + + // did we find a terminating '>'? + if (index >= length) { + if (!ignoreErrors) + throw new AddressException("Missing '>'", s, index); + // pretend the "<" was a regular character and + // continue parsing after it (e.g., ``<@foo.com'') + index = rindex + 1; + if (start == -1) + start = rindex; // back up to include "<" + break; + } + + if (!in_group) { + if (start >= 0) { + // seen some characters? use them as the personal name + start_personal = start; + end_personal = rindex; + } + start = rindex + 1; + } + route_addr = true; + end = index; + break; + + case '>': + if (!ignoreErrors) + throw new AddressException("Missing '<'", s, index); + // pretend the ">" was a regular character and + // continue parsing (e.g., ``>@foo.com'') + if (start == -1) + start = index; + break; + + case '"': // parse quoted string + int qindex = index; + rfc822 = true; + if (start == -1) + start = index; + outq: + for (index++; index < length; index++) { + c = s.charAt(index); + switch (c) { + case '\\': + index++; // skip both '\' and the escaped char + break; + case '"': + break outq; // out of for loop + default: + break; + } + } + if (index >= length) { + if (!ignoreErrors) + throw new AddressException("Missing '\"'", s, index); + // pretend the quote was a regular character and + // continue parsing after it (e.g., ``"@foo.com'') + index = qindex + 1; + } + break; + + case '[': // a domain-literal, probably + int lindex = index; + rfc822 = true; + if (start == -1) + start = index; + outb: + for (index++; index < length; index++) { + c = s.charAt(index); + switch (c) { + case '\\': + index++; // skip both '\' and the escaped char + break; + case ']': + break outb; // out of for loop + default: + break; + } + } + if (index >= length) { + if (!ignoreErrors) + throw new AddressException("Missing ']'", s, index); + // pretend the "[" was a regular character and + // continue parsing after it (e.g., ``[@foo.com'') + index = lindex + 1; + } + break; + + case ';': + if (start == -1) { + route_addr = false; + rfc822 = false; + start = end = -1; + break; // nope, nothing there + } + if (in_group) { + in_group = false; + /* + * If parsing headers, but not strictly, peek ahead. + * If next char is "@", treat the group name + * like the local part of the address, e.g., + * "Undisclosed-Recipient:;@java.sun.com". + */ + if (parseHdr && !strict && + index + 1 < length && s.charAt(index + 1) == '@') + break; + ma = new InternetAddress(); + end = index + 1; + ma.setAddress(s.substring(start, end).trim()); + v.add(ma); + + route_addr = false; + rfc822 = false; + start = end = -1; + start_personal = end_personal = -1; + break; + } + if (!ignoreErrors) + throw new AddressException( + "Illegal semicolon, not in group", s, index); + + // otherwise, parsing a header; treat semicolon like comma + // fall through to comma case... + + case ',': // end of an address, probably + if (start == -1) { + route_addr = false; + rfc822 = false; + start = end = -1; + break; // nope, nothing there + } + if (in_group) { + route_addr = false; + break; + } + // got a token, add this to our InternetAddress list + if (end == -1) + end = index; + + String addr = s.substring(start, end).trim(); + String pers = null; + if (rfc822 && start_personal >= 0) { + pers = unquote( + s.substring(start_personal, end_personal).trim()); + if (pers.trim().length() == 0) + pers = null; + } + + /* + * If the personal name field has an "@" and the address + * field does not, assume they were reversed, e.g., + * ``"joe doe" (john.doe@example.com)''. + */ + if (parseHdr && !strict && pers != null && + pers.indexOf('@') >= 0 && + addr.indexOf('@') < 0 && addr.indexOf('!') < 0) { + String tmp = addr; + addr = pers; + pers = tmp; + } + if (rfc822 || strict || parseHdr) { + if (!ignoreErrors) + checkAddress(addr, route_addr, false); + ma = new InternetAddress(); + ma.setAddress(addr); + if (pers != null) + ma.encodedPersonal = pers; + v.add(ma); + } else { + // maybe we passed over more than one space-separated addr + StringTokenizer st = new StringTokenizer(addr); + while (st.hasMoreTokens()) { + String a = st.nextToken(); + checkAddress(a, false, false); + ma = new InternetAddress(); + ma.setAddress(a); + v.add(ma); + } + } + + route_addr = false; + rfc822 = false; + start = end = -1; + start_personal = end_personal = -1; + break; + + case ':': + rfc822 = true; + if (in_group) + if (!ignoreErrors) + throw new AddressException("Nested group", s, index); + if (start == -1) + start = index; + if (parseHdr && !strict) { + /* + * If next char is a special character that can't occur at + * the start of a valid address, treat the group name + * as the entire address, e.g., "Date:, Tue", "Re:@foo". + */ + if (index + 1 < length) { + String addressSpecials = ")>[]:@\\,."; + char nc = s.charAt(index + 1); + if (addressSpecials.indexOf(nc) >= 0) { + if (nc != '@') + break; // don't change in_group + /* + * Handle a common error: + * ``Undisclosed-Recipient:@example.com;'' + * + * Scan ahead. If we find a semicolon before + * one of these other special characters, + * consider it to be a group after all. + */ + for (int i = index + 2; i < length; i++) { + nc = s.charAt(i); + if (nc == ';') + break; + if (addressSpecials.indexOf(nc) >= 0) + break; + } + if (nc == ';') + break; // don't change in_group + } + } + + // ignore bogus "mailto:" prefix in front of an address, + // or bogus mail header name included in the address field + String gname = s.substring(start, index); + if (ignoreBogusGroupName && + (gname.equalsIgnoreCase("mailto") || + gname.equalsIgnoreCase("From") || + gname.equalsIgnoreCase("To") || + gname.equalsIgnoreCase("Cc") || + gname.equalsIgnoreCase("Subject") || + gname.equalsIgnoreCase("Re"))) + start = -1; // we're not really in a group + else + in_group = true; + } else + in_group = true; + break; + + // Ignore whitespace + case ' ': + case '\t': + case '\r': + case '\n': + break; + + default: + if (start == -1) + start = index; + break; + } + } + + if (start >= 0) { + /* + * The last token, add this to our InternetAddress list. + * Note that this block of code should be identical to the + * block above for "case ','". + */ + if (end == -1) + end = length; + + String addr = s.substring(start, end).trim(); + String pers = null; + if (rfc822 && start_personal >= 0) { + pers = unquote( + s.substring(start_personal, end_personal).trim()); + if (pers.trim().length() == 0) + pers = null; + } + + /* + * If the personal name field has an "@" and the address + * field does not, assume they were reversed, e.g., + * ``"joe doe" (john.doe@example.com)''. + */ + if (parseHdr && !strict && + pers != null && pers.indexOf('@') >= 0 && + addr.indexOf('@') < 0 && addr.indexOf('!') < 0) { + String tmp = addr; + addr = pers; + pers = tmp; + } + if (rfc822 || strict || parseHdr) { + if (!ignoreErrors) + checkAddress(addr, route_addr, false); + ma = new InternetAddress(); + ma.setAddress(addr); + if (pers != null) + ma.encodedPersonal = pers; + v.add(ma); + } else { + // maybe we passed over more than one space-separated addr + StringTokenizer st = new StringTokenizer(addr); + while (st.hasMoreTokens()) { + String a = st.nextToken(); + checkAddress(a, false, false); + ma = new InternetAddress(); + ma.setAddress(a); + v.add(ma); + } + } + } + + InternetAddress[] a = new InternetAddress[v.size()]; + v.toArray(a); + return a; + } + + /** + * Validate that this address conforms to the syntax rules of + * RFC 822. The current implementation checks many, but not + * all, syntax rules. Note that even though the syntax of + * the address may be correct, there's no guarantee that a + * mailbox of that name exists. + * + * @exception AddressException if the address isn't valid. + * @since JavaMail 1.3 + */ + public void validate() throws AddressException { + if (isGroup()) + getGroup(true); // throw away the result + else + checkAddress(getAddress(), true, true); + } + + private static final String specialsNoDotNoAt = "()<>,;:\\\"[]"; + private static final String specialsNoDot = specialsNoDotNoAt + "@"; + + /** + * Check that the address is a valid "mailbox" per RFC822. + * (We also allow simple names.) + * + * XXX - much more to check + * XXX - doesn't handle domain-literals properly (but no one uses them) + */ + private static void checkAddress(String addr, + boolean routeAddr, boolean validate) + throws AddressException { + int i, start = 0; + + if (addr == null) + throw new AddressException("Address is null"); + int len = addr.length(); + if (len == 0) + throw new AddressException("Empty address", addr); + + /* + * routeAddr indicates that the address is allowed + * to have an RFC 822 "route". + */ + if (routeAddr && addr.charAt(0) == '@') { + /* + * Check for a legal "route-addr": + * [@domain[,@domain ...]:]local@domain + */ + for (start = 0; (i = indexOfAny(addr, ",:", start)) >= 0; + start = i+1) { + if (addr.charAt(start) != '@') + throw new AddressException("Illegal route-addr", addr); + if (addr.charAt(i) == ':') { + // end of route-addr + start = i + 1; + break; + } + } + } + + /* + * The rest should be "local@domain", but we allow simply "local" + * unless called from validate. + * + * local-part must follow RFC 822 - no specials except '.' + * unless quoted. + */ + + char c = (char)-1; + char lastc = (char)-1; + boolean inquote = false; + for (i = start; i < len; i++) { + lastc = c; + c = addr.charAt(i); + // a quoted-pair is only supposed to occur inside a quoted string, + // but some people use it outside so we're more lenient + if (c == '\\' || lastc == '\\') + continue; + if (c == '"') { + if (inquote) { + // peek ahead, next char must be "@" + if (validate && i + 1 < len && addr.charAt(i + 1) != '@') + throw new AddressException( + "Quote not at end of local address", addr); + inquote = false; + } else { + if (validate && i != 0) + throw new AddressException( + "Quote not at start of local address", addr); + inquote = true; + } + continue; + } else if (c == '\r') { + // peek ahead, next char must be LF + if (i + 1 < len && addr.charAt(i + 1) != '\n') + throw new AddressException( + "Quoted local address contains CR without LF", addr); + } else if (c == '\n') { + /* + * CRLF followed by whitespace is allowed in a quoted string. + * We allowed naked LF, but ensure LF is always followed by + * whitespace to prevent spoofing the end of the header. + */ + if (i + 1 < len && addr.charAt(i + 1) != ' ' && + addr.charAt(i + 1) != '\t') + throw new AddressException( + "Quoted local address contains newline without whitespace", + addr); + } else if (c == '.') { + if (i == start) + throw new AddressException( + "Local address starts with dot", addr); + if (lastc == '.') + throw new AddressException( + "Local address contains dot-dot", addr); + } + if (inquote) + continue; + if (c == '@') { + if (i == 0) + throw new AddressException("Missing local name", addr); + if (lastc == '.') + throw new AddressException( + "Local address ends with dot", addr); + break; // done with local part + } + if (c <= 040 || c == 0177) + throw new AddressException( + "Local address contains control or whitespace", addr); + if (specialsNoDot.indexOf(c) >= 0) + throw new AddressException( + "Local address contains illegal character", addr); + } + if (inquote) + throw new AddressException("Unterminated quote", addr); + + /* + * Done with local part, now check domain. + * + * Note that the MimeMessage class doesn't remember addresses + * as separate objects; it writes them out as headers and then + * parses the headers when the addresses are requested. + * In order to support the case where a "simple" address is used, + * but the address also has a personal name and thus looks like + * it should be a valid RFC822 address when parsed, we only check + * this if we're explicitly called from the validate method. + */ + + if (c != '@') { + if (validate) + throw new AddressException("Missing final '@domain'", addr); + return; + } + + // check for illegal chars in the domain, but ignore domain literals + + start = i + 1; + if (start >= len) + throw new AddressException("Missing domain", addr); + + if (addr.charAt(start) == '.') + throw new AddressException("Domain starts with dot", addr); + boolean inliteral = false; + for (i = start; i < len; i++) { + c = addr.charAt(i); + if (c == '[') { + if (i != start) + throw new AddressException( + "Domain literal not at start of domain", addr); + inliteral = true; // domain literal, don't validate + } else if (c == ']') { + if (i != len - 1) + throw new AddressException( + "Domain literal end not at end of domain", addr); + inliteral = false; + } else if (c <= 040 || c == 0177) { + throw new AddressException( + "Domain contains control or whitespace", addr); + } else { + // RFC 2822 rule + //if (specialsNoDot.indexOf(c) >= 0) + /* + * RFC 1034 rule is more strict + * the full rule is: + * + * ::= | " " + * ::=

    + * + * This class is mostly intended for service providers. MimeMessage + * and MimeBody use this class for holding their headers. + * + *


    A note on RFC822 and MIME headers

    + * + * RFC822 and MIME header fields must contain only + * US-ASCII characters. If a header contains non US-ASCII characters, + * it must be encoded as per the rules in RFC 2047. The MimeUtility + * class provided in this package can be used to to achieve this. + * Callers of the setHeader, addHeader, and + * addHeaderLine methods are responsible for enforcing + * the MIME requirements for the specified headers. In addition, these + * header fields must be folded (wrapped) before being sent if they + * exceed the line length limitation for the transport (1000 bytes for + * SMTP). Received headers may have been folded. The application is + * responsible for folding and unfolding headers as appropriate.

    + * + * The current implementation supports the System property + * mail.mime.ignorewhitespacelines, which if set to true + * will cause a line containing only whitespace to be considered + * a blank line terminating the header. + * + * @see javax.mail.internet.MimeUtility + * @author John Mani + * @author Bill Shannon + */ + +public class InternetHeaders { + private static final boolean ignoreWhitespaceLines = + PropUtil.getBooleanSystemProperty("mail.mime.ignorewhitespacelines", + false); + + /** + * An individual internet header. This class is only used by + * subclasses of InternetHeaders.

    + * + * An InternetHeader object with a null value is used as a placeholder + * for headers of that name, to preserve the order of headers. + * A placeholder InternetHeader object with a name of ":" marks + * the location in the list of headers where new headers are + * added by default. + * + * @since JavaMail 1.4 + */ + protected static final class InternetHeader extends Header { + /* + * Note that the value field from the superclass + * isn't used in this class. We extract the value + * from the line field as needed. We store the line + * rather than just the value to ensure that we can + * get back the exact original line, with the original + * whitespace, etc. + */ + String line; // the entire RFC822 header "line", + // or null if placeholder + + /** + * Constructor that takes a line and splits out + * the header name. + * + * @param l the header line + */ + public InternetHeader(String l) { + super("", ""); // XXX - we'll change it later + int i = l.indexOf(':'); + if (i < 0) { + // should never happen + name = l.trim(); + } else { + name = l.substring(0, i).trim(); + } + line = l; + } + + /** + * Constructor that takes a header name and value. + * + * @param n the name of the header + * @param v the value of the header + */ + public InternetHeader(String n, String v) { + super(n, ""); + if (v != null) + line = n + ": " + v; + else + line = null; + } + + /** + * Return the "value" part of the header line. + */ + @Override + public String getValue() { + int i = line.indexOf(':'); + if (i < 0) + return line; + // skip whitespace after ':' + int j; + for (j = i + 1; j < line.length(); j++) { + char c = line.charAt(j); + if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n')) + break; + } + return line.substring(j); + } + } + + /* + * The enumeration object used to enumerate an + * InternetHeaders object. Can return + * either a String or a Header object. + */ + static class MatchEnum { + private Iterator e; // enum object of headers List + // XXX - is this overkill? should we step through in index + // order instead? + private String names[]; // names to match, or not + private boolean match; // return matching headers? + private boolean want_line; // return header lines? + private InternetHeader next_header; // the next header to be returned + + /* + * Constructor. Initialize the enumeration for the entire + * List of headers, the set of headers, whether to return + * matching or non-matching headers, and whether to return + * header lines or Header objects. + */ + MatchEnum(List v, String n[], boolean m, boolean l) { + e = v.iterator(); + names = n; + match = m; + want_line = l; + next_header = null; + } + + /* + * Any more elements in this enumeration? + */ + public boolean hasMoreElements() { + // if necessary, prefetch the next matching header, + // and remember it. + if (next_header == null) + next_header = nextMatch(); + return next_header != null; + } + + /* + * Return the next element. + */ + public Object nextElement() { + if (next_header == null) + next_header = nextMatch(); + + if (next_header == null) + throw new NoSuchElementException("No more headers"); + + InternetHeader h = next_header; + next_header = null; + if (want_line) + return h.line; + else + return new Header(h.getName(), h.getValue()); + } + + /* + * Return the next Header object according to the match + * criteria, or null if none left. + */ + private InternetHeader nextMatch() { + next: + while (e.hasNext()) { + InternetHeader h = e.next(); + + // skip "place holder" headers + if (h.line == null) + continue; + + // if no names to match against, return appropriately + if (names == null) + return match ? null : h; + + // check whether this header matches any of the names + for (int i = 0; i < names.length; i++) { + if (names[i].equalsIgnoreCase(h.getName())) { + if (match) + return h; + else + // found a match, but we're + // looking for non-matches. + // try next header. + continue next; + } + } + // found no matches. if that's what we wanted, return it. + if (!match) + return h; + } + return null; + } + } + + static class MatchStringEnum extends MatchEnum + implements Enumeration { + + MatchStringEnum(List v, String[] n, boolean m) { + super(v, n, m, true); + } + + @Override + public String nextElement() { + return (String) super.nextElement(); + } + + } + + static class MatchHeaderEnum extends MatchEnum + implements Enumeration

    { + + MatchHeaderEnum(List v, String[] n, boolean m) { + super(v, n, m, false); + } + + @Override + public Header nextElement() { + return (Header) super.nextElement(); + } + + } + + /** + * The actual list of Headers, including placeholder entries. + * Placeholder entries are Headers with a null value and + * are never seen by clients of the InternetHeaders class. + * Placeholder entries are used to keep track of the preferred + * order of headers. Headers are never actually removed from + * the list, they're converted into placeholder entries. + * New headers are added after existing headers of the same name + * (or before in the case of Received and + * Return-Path headers). If no existing header + * or placeholder for the header is found, new headers are + * added after the special placeholder with the name ":". + * + * @since JavaMail 1.4 + */ + protected List headers; + + /** + * Create an empty InternetHeaders object. Placeholder entries + * are inserted to indicate the preferred order of headers. + */ + public InternetHeaders() { + headers = new ArrayList<>(40); + headers.add(new InternetHeader("Return-Path", null)); + headers.add(new InternetHeader("Received", null)); + headers.add(new InternetHeader("Resent-Date", null)); + headers.add(new InternetHeader("Resent-From", null)); + headers.add(new InternetHeader("Resent-Sender", null)); + headers.add(new InternetHeader("Resent-To", null)); + headers.add(new InternetHeader("Resent-Cc", null)); + headers.add(new InternetHeader("Resent-Bcc", null)); + headers.add(new InternetHeader("Resent-Message-Id", null)); + headers.add(new InternetHeader("Date", null)); + headers.add(new InternetHeader("From", null)); + headers.add(new InternetHeader("Sender", null)); + headers.add(new InternetHeader("Reply-To", null)); + headers.add(new InternetHeader("To", null)); + headers.add(new InternetHeader("Cc", null)); + headers.add(new InternetHeader("Bcc", null)); + headers.add(new InternetHeader("Message-Id", null)); + headers.add(new InternetHeader("In-Reply-To", null)); + headers.add(new InternetHeader("References", null)); + headers.add(new InternetHeader("Subject", null)); + headers.add(new InternetHeader("Comments", null)); + headers.add(new InternetHeader("Keywords", null)); + headers.add(new InternetHeader("Errors-To", null)); + headers.add(new InternetHeader("MIME-Version", null)); + headers.add(new InternetHeader("Content-Type", null)); + headers.add(new InternetHeader("Content-Transfer-Encoding", null)); + headers.add(new InternetHeader("Content-MD5", null)); + headers.add(new InternetHeader(":", null)); + headers.add(new InternetHeader("Content-Length", null)); + headers.add(new InternetHeader("Status", null)); + } + + /** + * Read and parse the given RFC822 message stream till the + * blank line separating the header from the body. The input + * stream is left positioned at the start of the body. The + * header lines are stored internally.

    + * + * For efficiency, wrap a BufferedInputStream around the actual + * input stream and pass it as the parameter.

    + * + * No placeholder entries are inserted; the original order of + * the headers is preserved. + * + * @param is RFC822 input stream + * @exception MessagingException for any I/O error reading the stream + */ + public InternetHeaders(InputStream is) throws MessagingException { + this(is, false); + } + + /** + * Read and parse the given RFC822 message stream till the + * blank line separating the header from the body. The input + * stream is left positioned at the start of the body. The + * header lines are stored internally.

    + * + * For efficiency, wrap a BufferedInputStream around the actual + * input stream and pass it as the parameter.

    + * + * No placeholder entries are inserted; the original order of + * the headers is preserved. + * + * @param is RFC822 input stream + * @param allowutf8 if UTF-8 encoded headers are allowed + * @exception MessagingException for any I/O error reading the stream + * @since JavaMail 1.6 + */ + public InternetHeaders(InputStream is, boolean allowutf8) + throws MessagingException { + headers = new ArrayList<>(40); + load(is, allowutf8); + } + + /** + * Read and parse the given RFC822 message stream till the + * blank line separating the header from the body. Store the + * header lines inside this InternetHeaders object. The order + * of header lines is preserved.

    + * + * Note that the header lines are added into this InternetHeaders + * object, so any existing headers in this object will not be + * affected. Headers are added to the end of the existing list + * of headers, in order. + * + * @param is RFC822 input stream + * @exception MessagingException for any I/O error reading the stream + */ + public void load(InputStream is) throws MessagingException { + load(is, false); + } + + /** + * Read and parse the given RFC822 message stream till the + * blank line separating the header from the body. Store the + * header lines inside this InternetHeaders object. The order + * of header lines is preserved.

    + * + * Note that the header lines are added into this InternetHeaders + * object, so any existing headers in this object will not be + * affected. Headers are added to the end of the existing list + * of headers, in order. + * + * @param is RFC822 input stream + * @param allowutf8 if UTF-8 encoded headers are allowed + * @exception MessagingException for any I/O error reading the stream + * @since JavaMail 1.6 + */ + public void load(InputStream is, boolean allowutf8) + throws MessagingException { + // Read header lines until a blank line. It is valid + // to have BodyParts with no header lines. + String line; + LineInputStream lis = new LineInputStream(is, allowutf8); + String prevline = null; // the previous header line, as a string + // a buffer to accumulate the header in, when we know it's needed + StringBuilder lineBuffer = new StringBuilder(); + + try { + // if the first line being read is a continuation line, + // we ignore it if it's otherwise empty or we treat it as + // a non-continuation line if it has non-whitespace content + boolean first = true; + do { + line = lis.readLine(); + if (line != null && + (line.startsWith(" ") || line.startsWith("\t"))) { + // continuation of header + if (prevline != null) { + lineBuffer.append(prevline); + prevline = null; + } + if (first) { + String lt = line.trim(); + if (lt.length() > 0) + lineBuffer.append(lt); + } else { + if (lineBuffer.length() > 0) + lineBuffer.append("\r\n"); + lineBuffer.append(line); + } + } else { + // new header + if (prevline != null) + addHeaderLine(prevline); + else if (lineBuffer.length() > 0) { + // store previous header first + addHeaderLine(lineBuffer.toString()); + lineBuffer.setLength(0); + } + prevline = line; + } + first = false; + } while (line != null && !isEmpty(line)); + } catch (IOException ioex) { + throw new MessagingException("Error in input stream", ioex); + } + } + + /** + * Is this line an empty (blank) line? + */ + private static final boolean isEmpty(String line) { + return line.length() == 0 || + (ignoreWhitespaceLines && line.trim().length() == 0); + } + + /** + * Return all the values for the specified header. The + * values are String objects. Returns null + * if no headers with the specified name exist. + * + * @param name header name + * @return array of header values, or null if none + */ + public String[] getHeader(String name) { + Iterator e = headers.iterator(); + // XXX - should we just step through in index order? + List v = new ArrayList<>(); // accumulate return values + + while (e.hasNext()) { + InternetHeader h = e.next(); + if (name.equalsIgnoreCase(h.getName()) && h.line != null) { + v.add(h.getValue()); + } + } + if (v.size() == 0) + return (null); + // convert List to an array for return + String r[] = new String[v.size()]; + r = v.toArray(r); + return (r); + } + + /** + * Get all the headers for this header name, returned as a single + * String, with headers separated by the delimiter. If the + * delimiter is null, only the first header is + * returned. Returns null + * if no headers with the specified name exist. + * + * @param name header name + * @param delimiter delimiter + * @return the value fields for all headers with + * this name, or null if none + */ + public String getHeader(String name, String delimiter) { + String s[] = getHeader(name); + + if (s == null) + return null; + + if ((s.length == 1) || delimiter == null) + return s[0]; + + StringBuilder r = new StringBuilder(s[0]); + for (int i = 1; i < s.length; i++) { + r.append(delimiter); + r.append(s[i]); + } + return r.toString(); + } + + /** + * Change the first header line that matches name + * to have value, adding a new header if no existing header + * matches. Remove all matching headers but the first.

    + * + * Note that RFC822 headers can only contain US-ASCII characters + * + * @param name header name + * @param value header value + */ + public void setHeader(String name, String value) { + boolean found = false; + + for (int i = 0; i < headers.size(); i++) { + InternetHeader h = headers.get(i); + if (name.equalsIgnoreCase(h.getName())) { + if (!found) { + int j; + if (h.line != null && (j = h.line.indexOf(':')) >= 0) { + h.line = h.line.substring(0, j + 1) + " " + value; + // preserves capitalization, spacing + } else { + h.line = name + ": " + value; + } + found = true; + } else { + headers.remove(i); + i--; // have to look at i again + } + } + } + + if (!found) { + addHeader(name, value); + } + } + + /** + * Add a header with the specified name and value to the header list.

    + * + * The current implementation knows about the preferred order of most + * well-known headers and will insert headers in that order. In + * addition, it knows that Received headers should be + * inserted in reverse order (newest before oldest), and that they + * should appear at the beginning of the headers, preceeded only by + * a possible Return-Path header.

    + * + * Note that RFC822 headers can only contain US-ASCII characters. + * + * @param name header name + * @param value header value + */ + public void addHeader(String name, String value) { + int pos = headers.size(); + boolean addReverse = + name.equalsIgnoreCase("Received") || + name.equalsIgnoreCase("Return-Path"); + if (addReverse) + pos = 0; + for (int i = headers.size() - 1; i >= 0; i--) { + InternetHeader h = headers.get(i); + if (name.equalsIgnoreCase(h.getName())) { + if (addReverse) { + pos = i; + } else { + headers.add(i + 1, new InternetHeader(name, value)); + return; + } + } + // marker for default place to add new headers + if (!addReverse && h.getName().equals(":")) + pos = i; + } + headers.add(pos, new InternetHeader(name, value)); + } + + /** + * Remove all header entries that match the given name + * @param name header name + */ + public void removeHeader(String name) { + for (int i = 0; i < headers.size(); i++) { + InternetHeader h = headers.get(i); + if (name.equalsIgnoreCase(h.getName())) { + h.line = null; + //headers.remove(i); + //i--; // have to look at i again + } + } + } + + /** + * Return all the headers as an Enumeration of + * {@link javax.mail.Header} objects. + * + * @return Enumeration of Header objects + */ + public Enumeration

    getAllHeaders() { + return (new MatchHeaderEnum(headers, null, false)); + } + + /** + * Return all matching {@link javax.mail.Header} objects. + * + * @param names the headers to return + * @return Enumeration of matching Header objects + */ + public Enumeration
    getMatchingHeaders(String[] names) { + return (new MatchHeaderEnum(headers, names, true)); + } + + /** + * Return all non-matching {@link javax.mail.Header} objects. + * + * @param names the headers to not return + * @return Enumeration of non-matching Header objects + */ + public Enumeration
    getNonMatchingHeaders(String[] names) { + return (new MatchHeaderEnum(headers, names, false)); + } + + /** + * Add an RFC822 header line to the header store. + * If the line starts with a space or tab (a continuation line), + * add it to the last header line in the list. Otherwise, + * append the new header line to the list.

    + * + * Note that RFC822 headers can only contain US-ASCII characters + * + * @param line raw RFC822 header line + */ + public void addHeaderLine(String line) { + try { + char c = line.charAt(0); + if (c == ' ' || c == '\t') { + InternetHeader h = headers.get(headers.size() - 1); + h.line += "\r\n" + line; + } else + headers.add(new InternetHeader(line)); + } catch (StringIndexOutOfBoundsException e) { + // line is empty, ignore it + return; + } catch (NoSuchElementException e) { + // XXX - list is empty? + } + } + + /** + * Return all the header lines as an Enumeration of Strings. + * + * @return Enumeration of Strings of all header lines + */ + public Enumeration getAllHeaderLines() { + return (getNonMatchingHeaderLines(null)); + } + + /** + * Return all matching header lines as an Enumeration of Strings. + * + * @param names the headers to return + * @return Enumeration of Strings of all matching header lines + */ + public Enumeration getMatchingHeaderLines(String[] names) { + return (new MatchStringEnum(headers, names, true)); + } + + /** + * Return all non-matching header lines + * + * @param names the headers to not return + * @return Enumeration of Strings of all non-matching header lines + */ + public Enumeration getNonMatchingHeaderLines(String[] names) { + return (new MatchStringEnum(headers, names, false)); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/MailDateFormat.java b/fine-third-default/fine-mail/src/javax/mail/internet/MailDateFormat.java new file mode 100644 index 000000000..d2771ab1f --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/MailDateFormat.java @@ -0,0 +1,1066 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectStreamException; +import java.util.Date; +import java.util.Calendar; +import java.util.Locale; +import java.util.TimeZone; +import java.util.logging.Level; +import java.text.DateFormatSymbols; +import java.text.SimpleDateFormat; +import java.text.NumberFormat; +import java.text.FieldPosition; +import java.text.ParsePosition; +import java.text.ParseException; + +import com.sun.mail.util.MailLogger; + +/** + * Formats and parses date specification based on + * RFC 2822.

    + * + * This class does not support methods that influence the format. It always + * formats the date based on the specification below.

    + * + * 3.3. Date and Time Specification + *

    + * Date and time occur in several header fields. This section specifies + * the syntax for a full date and time specification. Though folding + * white space is permitted throughout the date-time specification, it is + * RECOMMENDED that a single space be used in each place that FWS appears + * (whether it is required or optional); some older implementations may + * not interpret other occurrences of folding white space correctly. + *

    + * date-time       =       [ day-of-week "," ] date FWS time [CFWS]
    + *
    + * day-of-week     =       ([FWS] day-name) / obs-day-of-week
    + *
    + * day-name        =       "Mon" / "Tue" / "Wed" / "Thu" /
    + *                         "Fri" / "Sat" / "Sun"
    + *
    + * date            =       day month year
    + *
    + * year            =       4*DIGIT / obs-year
    + *
    + * month           =       (FWS month-name FWS) / obs-month
    + *
    + * month-name      =       "Jan" / "Feb" / "Mar" / "Apr" /
    + *                         "May" / "Jun" / "Jul" / "Aug" /
    + *                         "Sep" / "Oct" / "Nov" / "Dec"
    + *
    + * day             =       ([FWS] 1*2DIGIT) / obs-day
    + *
    + * time            =       time-of-day FWS zone
    + *
    + * time-of-day     =       hour ":" minute [ ":" second ]
    + *
    + * hour            =       2DIGIT / obs-hour
    + *
    + * minute          =       2DIGIT / obs-minute
    + *
    + * second          =       2DIGIT / obs-second
    + *
    + * zone            =       (( "+" / "-" ) 4DIGIT) / obs-zone
    + * 
    + * The day is the numeric day of the month. The year is any numeric year + * 1900 or later. + *

    + * The time-of-day specifies the number of hours, minutes, and optionally + * seconds since midnight of the date indicated. + *

    + * The date and time-of-day SHOULD express local time. + *

    + * The zone specifies the offset from Coordinated Universal Time (UTC, + * formerly referred to as "Greenwich Mean Time") that the date and + * time-of-day represent. The "+" or "-" indicates whether the + * time-of-day is ahead of (i.e., east of) or behind (i.e., west of) + * Universal Time. The first two digits indicate the number of hours + * difference from Universal Time, and the last two digits indicate the + * number of minutes difference from Universal Time. (Hence, +hhmm means + * +(hh * 60 + mm) minutes, and -hhmm means -(hh * 60 + mm) minutes). The + * form "+0000" SHOULD be used to indicate a time zone at Universal Time. + * Though "-0000" also indicates Universal Time, it is used to indicate + * that the time was generated on a system that may be in a local time + * zone other than Universal Time and therefore indicates that the + * date-time contains no information about the local time zone. + *

    + * A date-time specification MUST be semantically valid. That is, the + * day-of-the-week (if included) MUST be the day implied by the date, the + * numeric day-of-month MUST be between 1 and the number of days allowed + * for the specified month (in the specified year), the time-of-day MUST + * be in the range 00:00:00 through 23:59:60 (the number of seconds + * allowing for a leap second; see [STD12]), and the zone MUST be within + * the range -9959 through +9959. + * + *

    Synchronization

    + * + *

    + * Date formats are not synchronized. + * It is recommended to create separate format instances for each thread. + * If multiple threads access a format concurrently, it must be synchronized + * externally. + * + * @author Anthony Vanelverdinghe + * @author Max Spivak + * @since JavaMail 1.2 + */ +public class MailDateFormat extends SimpleDateFormat { + + private static final long serialVersionUID = -8148227605210628779L; + private static final String PATTERN = "EEE, d MMM yyyy HH:mm:ss Z (z)"; + + private static final MailLogger LOGGER = new MailLogger( + MailDateFormat.class, "DEBUG", false, System.out); + + private static final int UNKNOWN_DAY_NAME = -1; + private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); + private static final int LEAP_SECOND = 60; + + /** + * Create a new date format for the RFC2822 specification with lenient + * parsing. + */ + public MailDateFormat() { + super(PATTERN, Locale.US); + } + + /** + * Allows to serialize instances such that they are deserializable with the + * previous implementation. + * + * @return the object to be serialized + * @throws ObjectStreamException never + */ + private Object writeReplace() throws ObjectStreamException { + MailDateFormat fmt = new MailDateFormat(); + fmt.superApplyPattern("EEE, d MMM yyyy HH:mm:ss 'XXXXX' (z)"); + fmt.setTimeZone(getTimeZone()); + return fmt; + } + + /** + * Allows to deserialize instances that were serialized with the previous + * implementation. + * + * @param in the stream containing the serialized object + * @throws IOException on read failures + * @throws ClassNotFoundException never + */ + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + super.applyPattern(PATTERN); + } + + /** + * Overrides Cloneable. + * + * @return a clone of this instance + * @since JavaMail 1.6 + */ + @Override + public MailDateFormat clone() { + return (MailDateFormat) super.clone(); + } + + /** + * Formats the given date in the format specified by + * RFC 2822 in the current TimeZone. + * + * @param date the Date object + * @param dateStrBuf the formatted string + * @param fieldPosition the current field position + * @return StringBuffer the formatted String + * @since JavaMail 1.2 + */ + @Override + public StringBuffer format(Date date, StringBuffer dateStrBuf, + FieldPosition fieldPosition) { + return super.format(date, dateStrBuf, fieldPosition); + } + + /** + * Parses the given date in the format specified by + * RFC 2822. + *

      + *
    • With strict parsing, obs-* tokens are unsupported. Lenient parsing + * supports obs-year and obs-zone, with the exception of the 1-character + * military time zones. + *
    • The optional CFWS token at the end is not parsed. + *
    • RFC 2822 specifies that a zone of "-0000" indicates that the + * date-time contains no information about the local time zone. This class + * uses the UTC time zone in this case. + *
    + * + * @param text the formatted date to be parsed + * @param pos the current parse position + * @return Date the parsed date. In case of error, returns null. + * @since JavaMail 1.2 + */ + @Override + public Date parse(String text, ParsePosition pos) { + if (text == null || pos == null) { + throw new NullPointerException(); + } else if (0 > pos.getIndex() || pos.getIndex() >= text.length()) { + return null; + } + + return isLenient() + ? new Rfc2822LenientParser(text, pos).parse() + : new Rfc2822StrictParser(text, pos).parse(); + } + + /** + * This method always throws an UnsupportedOperationException and should not + * be used because RFC 2822 mandates a specific calendar. + * + * @throws UnsupportedOperationException if this method is invoked + */ + @Override + public void setCalendar(Calendar newCalendar) { + throw new UnsupportedOperationException("Method " + + "setCalendar() shouldn't be called"); + } + + /** + * This method always throws an UnsupportedOperationException and should not + * be used because RFC 2822 mandates a specific number format. + * + * @throws UnsupportedOperationException if this method is invoked + */ + @Override + public void setNumberFormat(NumberFormat newNumberFormat) { + throw new UnsupportedOperationException("Method " + + "setNumberFormat() shouldn't be called"); + } + + /** + * This method always throws an UnsupportedOperationException and should not + * be used because RFC 2822 mandates a specific pattern. + * + * @throws UnsupportedOperationException if this method is invoked + * @since JavaMail 1.6 + */ + @Override + public void applyLocalizedPattern(String pattern) { + throw new UnsupportedOperationException("Method " + + "applyLocalizedPattern() shouldn't be called"); + } + + /** + * This method always throws an UnsupportedOperationException and should not + * be used because RFC 2822 mandates a specific pattern. + * + * @throws UnsupportedOperationException if this method is invoked + * @since JavaMail 1.6 + */ + @Override + public void applyPattern(String pattern) { + throw new UnsupportedOperationException("Method " + + "applyPattern() shouldn't be called"); + } + + /** + * This method allows serialization to change the pattern. + */ + private void superApplyPattern(String pattern) { + super.applyPattern(pattern); + } + + /** + * This method always throws an UnsupportedOperationException and should not + * be used because RFC 2822 mandates another strategy for interpreting + * 2-digits years. + * + * @return the start of the 100-year period into which two digit years are + * parsed + * @throws UnsupportedOperationException if this method is invoked + * @since JavaMail 1.6 + */ + @Override + public Date get2DigitYearStart() { + throw new UnsupportedOperationException("Method " + + "get2DigitYearStart() shouldn't be called"); + } + + /** + * This method always throws an UnsupportedOperationException and should not + * be used because RFC 2822 mandates another strategy for interpreting + * 2-digits years. + * + * @throws UnsupportedOperationException if this method is invoked + * @since JavaMail 1.6 + */ + @Override + public void set2DigitYearStart(Date startDate) { + throw new UnsupportedOperationException("Method " + + "set2DigitYearStart() shouldn't be called"); + } + + /** + * This method always throws an UnsupportedOperationException and should not + * be used because RFC 2822 mandates specific date format symbols. + * + * @throws UnsupportedOperationException if this method is invoked + * @since JavaMail 1.6 + */ + @Override + public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) { + throw new UnsupportedOperationException("Method " + + "setDateFormatSymbols() shouldn't be called"); + } + + /** + * Returns the date, as specified by the parameters. + * + * @param dayName + * @param day + * @param month + * @param year + * @param hour + * @param minute + * @param second + * @param zone + * @return the date, as specified by the parameters + * @throws IllegalArgumentException if this instance's Calendar is + * non-lenient and any of the parameters have invalid values, or if dayName + * is not consistent with day-month-year + */ + private Date toDate(int dayName, int day, int month, int year, + int hour, int minute, int second, int zone) { + if (second == LEAP_SECOND) { + second = 59; + } + + TimeZone tz = calendar.getTimeZone(); + try { + calendar.setTimeZone(UTC); + calendar.clear(); + calendar.set(year, month, day, hour, minute, second); + + if (dayName == UNKNOWN_DAY_NAME + || dayName == calendar.get(Calendar.DAY_OF_WEEK)) { + calendar.add(Calendar.MINUTE, zone); + return calendar.getTime(); + } else { + throw new IllegalArgumentException("Inconsistent day-name"); + } + } finally { + calendar.setTimeZone(tz); + } + } + + /** + * This class provides the building blocks for date parsing. + *

    + * It has the following invariants: + *

      + *
    • no exceptions are thrown, except for java.text.ParseException from + * parse* methods + *
    • when parse* throws ParseException OR get* returns INVALID_CHAR OR + * skip* returns false OR peek* is invoked, then pos.getIndex() on method + * exit is the same as it was on method entry + *
    + */ + private static abstract class AbstractDateParser { + + static final int INVALID_CHAR = -1; + static final int MAX_YEAR_DIGITS = 8; // guarantees that: + // year < new GregorianCalendar().getMaximum(Calendar.YEAR) + + final String text; + final ParsePosition pos; + + AbstractDateParser(String text, ParsePosition pos) { + this.text = text; + this.pos = pos; + } + + final Date parse() { + int startPosition = pos.getIndex(); + try { + return tryParse(); + } catch (Exception e) { // == ParseException | RuntimeException e + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log(Level.FINE, "Bad date: '" + text + "'", e); + } + pos.setErrorIndex(pos.getIndex()); + pos.setIndex(startPosition); + return null; + } + } + + abstract Date tryParse() throws ParseException; + + /** + * @return the java.util.Calendar constant for the parsed day name + */ + final int parseDayName() throws ParseException { + switch (getChar()) { + case 'S': + if (skipPair('u', 'n')) { + return Calendar.SUNDAY; + } else if (skipPair('a', 't')) { + return Calendar.SATURDAY; + } + break; + case 'T': + if (skipPair('u', 'e')) { + return Calendar.TUESDAY; + } else if (skipPair('h', 'u')) { + return Calendar.THURSDAY; + } + break; + case 'M': + if (skipPair('o', 'n')) { + return Calendar.MONDAY; + } + break; + case 'W': + if (skipPair('e', 'd')) { + return Calendar.WEDNESDAY; + } + break; + case 'F': + if (skipPair('r', 'i')) { + return Calendar.FRIDAY; + } + break; + case INVALID_CHAR: + throw new ParseException("Invalid day-name", + pos.getIndex()); + } + pos.setIndex(pos.getIndex() - 1); + throw new ParseException("Invalid day-name", pos.getIndex()); + } + + /** + * @return the java.util.Calendar constant for the parsed month name + */ + @SuppressWarnings("fallthrough") + final int parseMonthName(boolean caseSensitive) throws ParseException { + switch (getChar()) { + case 'j': + if (caseSensitive) { + break; + } + case 'J': + if (skipChar('u') || (!caseSensitive && skipChar('U'))) { + if (skipChar('l') || (!caseSensitive + && skipChar('L'))) { + return Calendar.JULY; + } else if (skipChar('n') || (!caseSensitive + && skipChar('N'))) { + return Calendar.JUNE; + } else { + pos.setIndex(pos.getIndex() - 1); + } + } else if (skipPair('a', 'n') || (!caseSensitive + && skipAlternativePair('a', 'A', 'n', 'N'))) { + return Calendar.JANUARY; + } + break; + case 'm': + if (caseSensitive) { + break; + } + case 'M': + if (skipChar('a') || (!caseSensitive && skipChar('A'))) { + if (skipChar('r') || (!caseSensitive + && skipChar('R'))) { + return Calendar.MARCH; + } else if (skipChar('y') || (!caseSensitive + && skipChar('Y'))) { + return Calendar.MAY; + } else { + pos.setIndex(pos.getIndex() - 1); + } + } + break; + case 'a': + if (caseSensitive) { + break; + } + case 'A': + if (skipPair('u', 'g') || (!caseSensitive + && skipAlternativePair('u', 'U', 'g', 'G'))) { + return Calendar.AUGUST; + } else if (skipPair('p', 'r') || (!caseSensitive + && skipAlternativePair('p', 'P', 'r', 'R'))) { + return Calendar.APRIL; + } + break; + case 'd': + if (caseSensitive) { + break; + } + case 'D': + if (skipPair('e', 'c') || (!caseSensitive + && skipAlternativePair('e', 'E', 'c', 'C'))) { + return Calendar.DECEMBER; + } + break; + case 'o': + if (caseSensitive) { + break; + } + case 'O': + if (skipPair('c', 't') || (!caseSensitive + && skipAlternativePair('c', 'C', 't', 'T'))) { + return Calendar.OCTOBER; + } + break; + case 's': + if (caseSensitive) { + break; + } + case 'S': + if (skipPair('e', 'p') || (!caseSensitive + && skipAlternativePair('e', 'E', 'p', 'P'))) { + return Calendar.SEPTEMBER; + } + break; + case 'n': + if (caseSensitive) { + break; + } + case 'N': + if (skipPair('o', 'v') || (!caseSensitive + && skipAlternativePair('o', 'O', 'v', 'V'))) { + return Calendar.NOVEMBER; + } + break; + case 'f': + if (caseSensitive) { + break; + } + case 'F': + if (skipPair('e', 'b') || (!caseSensitive + && skipAlternativePair('e', 'E', 'b', 'B'))) { + return Calendar.FEBRUARY; + } + break; + case INVALID_CHAR: + throw new ParseException("Invalid month", pos.getIndex()); + } + pos.setIndex(pos.getIndex() - 1); + throw new ParseException("Invalid month", pos.getIndex()); + } + + /** + * @return the number of minutes to be added to the time in the local + * time zone, in order to obtain the equivalent time in the UTC time + * zone. Returns 0 if the date-time contains no information about the + * local time zone. + */ + final int parseZoneOffset() throws ParseException { + int sign = getChar(); + if (sign == '+' || sign == '-') { + int offset = parseAsciiDigits(4, 4, true); + if (!isValidZoneOffset(offset)) { + pos.setIndex(pos.getIndex() - 5); + throw new ParseException("Invalid zone", pos.getIndex()); + } + + return ((sign == '+') ? -1 : 1) + * (offset / 100 * 60 + offset % 100); + } else if (sign != INVALID_CHAR) { + pos.setIndex(pos.getIndex() - 1); + } + throw new ParseException("Invalid zone", pos.getIndex()); + } + + boolean isValidZoneOffset(int offset) { + return (offset % 100) < 60; + } + + final int parseAsciiDigits(int count) throws ParseException { + return parseAsciiDigits(count, count); + } + + final int parseAsciiDigits(int min, int max) throws ParseException { + return parseAsciiDigits(min, max, false); + } + + final int parseAsciiDigits(int min, int max, boolean isEOF) + throws ParseException { + int result = 0; + int nbDigitsParsed = 0; + while (nbDigitsParsed < max && peekAsciiDigit()) { + result = result * 10 + getAsciiDigit(); + nbDigitsParsed++; + } + + if ((nbDigitsParsed < min) + || (nbDigitsParsed == max && !isEOF && peekAsciiDigit())) { + pos.setIndex(pos.getIndex() - nbDigitsParsed); + } else { + return result; + } + + String range = (min == max) + ? Integer.toString(min) + : "between " + min + " and " + max; + throw new ParseException("Invalid input: expected " + + range + " ASCII digits", pos.getIndex()); + } + + final void parseFoldingWhiteSpace() throws ParseException { + if (!skipFoldingWhiteSpace()) { + throw new ParseException("Invalid input: expected FWS", + pos.getIndex()); + } + } + + final void parseChar(char ch) throws ParseException { + if (!skipChar(ch)) { + throw new ParseException("Invalid input: expected '" + ch + "'", + pos.getIndex()); + } + } + + final int getAsciiDigit() { + int ch = getChar(); + if ('0' <= ch && ch <= '9') { + return Character.digit((char) ch, 10); + } else { + if (ch != INVALID_CHAR) { + pos.setIndex(pos.getIndex() - 1); + } + return INVALID_CHAR; + } + } + + final int getChar() { + if (pos.getIndex() < text.length()) { + char ch = text.charAt(pos.getIndex()); + pos.setIndex(pos.getIndex() + 1); + return ch; + } else { + return INVALID_CHAR; + } + } + + boolean skipFoldingWhiteSpace() { + // fast paths: a single ASCII space or no FWS + if (skipChar(' ')) { + if (!peekFoldingWhiteSpace()) { + return true; + } else { + pos.setIndex(pos.getIndex() - 1); + } + } else if (!peekFoldingWhiteSpace()) { + return false; + } + + // normal path + int startIndex = pos.getIndex(); + if (skipWhiteSpace()) { + while (skipNewline()) { + if (!skipWhiteSpace()) { + pos.setIndex(startIndex); + return false; + } + } + return true; + } else if (skipNewline() && skipWhiteSpace()) { + return true; + } else { + pos.setIndex(startIndex); + return false; + } + } + + final boolean skipWhiteSpace() { + int startIndex = pos.getIndex(); + while (skipAlternative(' ', '\t')) { /* empty */ } + return pos.getIndex() > startIndex; + } + + final boolean skipNewline() { + return skipPair('\r', '\n'); + } + + final boolean skipAlternativeTriple( + char firstStandard, char firstAlternative, + char secondStandard, char secondAlternative, + char thirdStandard, char thirdAlternative + ) { + if (skipAlternativePair(firstStandard, firstAlternative, + secondStandard, secondAlternative)) { + if (skipAlternative(thirdStandard, thirdAlternative)) { + return true; + } else { + pos.setIndex(pos.getIndex() - 2); + } + } + return false; + } + + final boolean skipAlternativePair( + char firstStandard, char firstAlternative, + char secondStandard, char secondAlternative + ) { + if (skipAlternative(firstStandard, firstAlternative)) { + if (skipAlternative(secondStandard, secondAlternative)) { + return true; + } else { + pos.setIndex(pos.getIndex() - 1); + } + } + return false; + } + + final boolean skipAlternative(char standard, char alternative) { + return skipChar(standard) || skipChar(alternative); + } + + final boolean skipPair(char first, char second) { + if (skipChar(first)) { + if (skipChar(second)) { + return true; + } else { + pos.setIndex(pos.getIndex() - 1); + } + } + return false; + } + + final boolean skipChar(char ch) { + if (pos.getIndex() < text.length() + && text.charAt(pos.getIndex()) == ch) { + pos.setIndex(pos.getIndex() + 1); + return true; + } else { + return false; + } + } + + final boolean peekAsciiDigit() { + return (pos.getIndex() < text.length() + && '0' <= text.charAt(pos.getIndex()) + && text.charAt(pos.getIndex()) <= '9'); + } + + boolean peekFoldingWhiteSpace() { + return (pos.getIndex() < text.length() + && (text.charAt(pos.getIndex()) == ' ' + || text.charAt(pos.getIndex()) == '\t' + || text.charAt(pos.getIndex()) == '\r')); + } + + final boolean peekChar(char ch) { + return (pos.getIndex() < text.length() + && text.charAt(pos.getIndex()) == ch); + } + + } + + private class Rfc2822StrictParser extends AbstractDateParser { + + Rfc2822StrictParser(String text, ParsePosition pos) { + super(text, pos); + } + + @Override + Date tryParse() throws ParseException { + int dayName = parseOptionalBegin(); + + int day = parseDay(); + int month = parseMonth(); + int year = parseYear(); + + parseFoldingWhiteSpace(); + + int hour = parseHour(); + parseChar(':'); + int minute = parseMinute(); + int second = (skipChar(':')) ? parseSecond() : 0; + + parseFwsBetweenTimeOfDayAndZone(); + + int zone = parseZone(); + + try { + return MailDateFormat.this.toDate(dayName, day, month, year, + hour, minute, second, zone); + } catch (IllegalArgumentException e) { + throw new ParseException("Invalid input: some of the calendar " + + "fields have invalid values, or day-name is " + + "inconsistent with date", pos.getIndex()); + } + } + + /** + * @return the java.util.Calendar constant for the parsed day name, or + * UNKNOWN_DAY_NAME iff the begin is missing + */ + int parseOptionalBegin() throws ParseException { + int dayName; + if (!peekAsciiDigit()) { + skipFoldingWhiteSpace(); + dayName = parseDayName(); + parseChar(','); + } else { + dayName = UNKNOWN_DAY_NAME; + } + return dayName; + } + + int parseDay() throws ParseException { + skipFoldingWhiteSpace(); + return parseAsciiDigits(1, 2); + } + + /** + * @return the java.util.Calendar constant for the parsed month name + */ + int parseMonth() throws ParseException { + parseFwsInMonth(); + int month = parseMonthName(isMonthNameCaseSensitive()); + parseFwsInMonth(); + return month; + } + + void parseFwsInMonth() throws ParseException { + parseFoldingWhiteSpace(); + } + + boolean isMonthNameCaseSensitive() { + return true; + } + + int parseYear() throws ParseException { + int year = parseAsciiDigits(4, MAX_YEAR_DIGITS); + if (year >= 1900) { + return year; + } else { + pos.setIndex(pos.getIndex() - 4); + while (text.charAt(pos.getIndex() - 1) == '0') { + pos.setIndex(pos.getIndex() - 1); + } + throw new ParseException("Invalid year", pos.getIndex()); + } + } + + int parseHour() throws ParseException { + return parseAsciiDigits(2); + } + + int parseMinute() throws ParseException { + return parseAsciiDigits(2); + } + + int parseSecond() throws ParseException { + return parseAsciiDigits(2); + } + + void parseFwsBetweenTimeOfDayAndZone() throws ParseException { + parseFoldingWhiteSpace(); + } + + int parseZone() throws ParseException { + return parseZoneOffset(); + } + + } + + private class Rfc2822LenientParser extends Rfc2822StrictParser { + + private Boolean hasDefaultFws; + + Rfc2822LenientParser(String text, ParsePosition pos) { + super(text, pos); + } + + @Override + int parseOptionalBegin() { + while (pos.getIndex() < text.length() && !peekAsciiDigit()) { + pos.setIndex(pos.getIndex() + 1); + } + + return UNKNOWN_DAY_NAME; + } + + @Override + int parseDay() throws ParseException { + skipFoldingWhiteSpace(); + return parseAsciiDigits(1, 3); + } + + @Override + void parseFwsInMonth() throws ParseException { + // '-' is allowed to accomodate for the date format as specified in + // RFC 3501 + if (hasDefaultFws == null) { + hasDefaultFws = !skipChar('-'); + skipFoldingWhiteSpace(); + } else if (hasDefaultFws) { + skipFoldingWhiteSpace(); + } else { + parseChar('-'); + } + } + + @Override + boolean isMonthNameCaseSensitive() { + return false; + } + + @Override + int parseYear() throws ParseException { + int year = parseAsciiDigits(1, MAX_YEAR_DIGITS); + if (year >= 1000) { + return year; + } else if (year >= 50) { + return year + 1900; + } else { + return year + 2000; + } + } + + @Override + int parseHour() throws ParseException { + return parseAsciiDigits(1, 2); + } + + @Override + int parseMinute() throws ParseException { + return parseAsciiDigits(1, 2); + } + + @Override + int parseSecond() throws ParseException { + return parseAsciiDigits(1, 2); + } + + @Override + void parseFwsBetweenTimeOfDayAndZone() throws ParseException { + skipFoldingWhiteSpace(); + } + + @Override + int parseZone() throws ParseException { + try { + if (pos.getIndex() >= text.length()) { + throw new ParseException("Missing zone", pos.getIndex()); + } + + if (peekChar('+') || peekChar('-')) { + return parseZoneOffset(); + } else if (skipAlternativePair('U', 'u', 'T', 't')) { + return 0; + } else if (skipAlternativeTriple('G', 'g', 'M', 'm', + 'T', 't')) { + return 0; + } else { + int hoursOffset; + if (skipAlternative('E', 'e')) { + hoursOffset = 4; + } else if (skipAlternative('C', 'c')) { + hoursOffset = 5; + } else if (skipAlternative('M', 'm')) { + hoursOffset = 6; + } else if (skipAlternative('P', 'p')) { + hoursOffset = 7; + } else { + throw new ParseException("Invalid zone", + pos.getIndex()); + } + if (skipAlternativePair('S', 's', 'T', 't')) { + hoursOffset += 1; + } else if (skipAlternativePair('D', 'd', 'T', 't')) { + } else { + pos.setIndex(pos.getIndex() - 1); + throw new ParseException("Invalid zone", + pos.getIndex()); + } + return hoursOffset * 60; + } + } catch (ParseException e) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log(Level.FINE, "No timezone? : '" + text + "'", e); + } + + return 0; + } + } + + @Override + boolean isValidZoneOffset(int offset) { + return true; + } + + @Override + boolean skipFoldingWhiteSpace() { + boolean result = peekFoldingWhiteSpace(); + + skipLoop: + while (pos.getIndex() < text.length()) { + switch (text.charAt(pos.getIndex())) { + case ' ': + case '\t': + case '\r': + case '\n': + pos.setIndex(pos.getIndex() + 1); + break; + default: + break skipLoop; + } + } + + return result; + } + + @Override + boolean peekFoldingWhiteSpace() { + return super.peekFoldingWhiteSpace() + || (pos.getIndex() < text.length() + && text.charAt(pos.getIndex()) == '\n'); + } + + } + +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/MimeBodyPart.java b/fine-third-default/fine-mail/src/javax/mail/internet/MimeBodyPart.java new file mode 100644 index 000000000..e04cbe9ef --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/MimeBodyPart.java @@ -0,0 +1,1736 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import javax.mail.*; +import javax.activation.*; +import java.io.*; +import java.util.*; +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.util.MimeUtil; +import com.sun.mail.util.MessageRemovedIOException; +import com.sun.mail.util.FolderClosedIOException; +import com.sun.mail.util.LineOutputStream; + +/** + * This class represents a MIME body part. It implements the + * BodyPart abstract class and the MimePart + * interface. MimeBodyParts are contained in MimeMultipart + * objects.

    + * + * MimeBodyPart uses the InternetHeaders class to parse + * and store the headers of that body part. + * + *


    A note on RFC 822 and MIME headers

    + * + * RFC 822 header fields must contain only + * US-ASCII characters. MIME allows non ASCII characters to be present + * in certain portions of certain headers, by encoding those characters. + * RFC 2047 specifies the rules for doing this. The MimeUtility + * class provided in this package can be used to to achieve this. + * Callers of the setHeader, addHeader, and + * addHeaderLine methods are responsible for enforcing + * the MIME requirements for the specified headers. In addition, these + * header fields must be folded (wrapped) before being sent if they + * exceed the line length limitation for the transport (1000 bytes for + * SMTP). Received headers may have been folded. The application is + * responsible for folding and unfolding headers as appropriate.

    + * + * @author John Mani + * @author Bill Shannon + * @author Kanwar Oberoi + * @see javax.mail.Part + * @see javax.mail.internet.MimePart + * @see javax.mail.internet.MimeUtility + */ + +public class MimeBodyPart extends BodyPart implements MimePart { + + // Paranoia: + // allow this last minute change to be disabled if it causes problems + private static final boolean setDefaultTextCharset = + PropUtil.getBooleanSystemProperty( + "mail.mime.setdefaulttextcharset", true); + + private static final boolean setContentTypeFileName = + PropUtil.getBooleanSystemProperty( + "mail.mime.setcontenttypefilename", true); + + private static final boolean encodeFileName = + PropUtil.getBooleanSystemProperty("mail.mime.encodefilename", false); + private static final boolean decodeFileName = + PropUtil.getBooleanSystemProperty("mail.mime.decodefilename", false); + private static final boolean ignoreMultipartEncoding = + PropUtil.getBooleanSystemProperty( + "mail.mime.ignoremultipartencoding", true); + private static final boolean allowutf8 = + PropUtil.getBooleanSystemProperty("mail.mime.allowutf8", true); + + // Paranoia: + // allow this last minute change to be disabled if it causes problems + static final boolean cacheMultipart = // accessed by MimeMessage + PropUtil.getBooleanSystemProperty("mail.mime.cachemultipart", true); + + + /** + * The DataHandler object representing this Part's content. + */ + protected DataHandler dh; + + /** + * Byte array that holds the bytes of the content of this Part. + */ + protected byte[] content; + + /** + * If the data for this body part was supplied by an + * InputStream that implements the SharedInputStream interface, + * contentStream is another such stream representing + * the content of this body part. In this case, content + * will be null. + * + * @since JavaMail 1.2 + */ + protected InputStream contentStream; + + /** + * The InternetHeaders object that stores all the headers + * of this body part. + */ + protected InternetHeaders headers; + + /** + * If our content is a Multipart of Message object, we save it + * the first time it's created by parsing a stream so that changes + * to the contained objects will not be lost. + * + * If this field is not null, it's return by the {@link #getContent} + * method. The {@link #getContent} method sets this field if it + * would return a Multipart or MimeMessage object. This field is + * is cleared by the {@link #setDataHandler} method. + * + * @since JavaMail 1.5 + */ + protected Object cachedContent; + + /** + * An empty MimeBodyPart object is created. + * This body part maybe filled in by a client constructing a multipart + * message. + */ + public MimeBodyPart() { + super(); + headers = new InternetHeaders(); + } + + /** + * Constructs a MimeBodyPart by reading and parsing the data from + * the specified input stream. The parser consumes data till the end + * of the given input stream. The input stream must start at the + * beginning of a valid MIME body part and must terminate at the end + * of that body part.

    + * + * Note that the "boundary" string that delimits body parts must + * not be included in the input stream. The intention + * is that the MimeMultipart parser will extract each body part's bytes + * from a multipart stream and feed them into this constructor, without + * the delimiter strings. + * + * @param is the body part Input Stream + * @exception MessagingException for failures + */ + public MimeBodyPart(InputStream is) throws MessagingException { + if (!(is instanceof ByteArrayInputStream) && + !(is instanceof BufferedInputStream) && + !(is instanceof SharedInputStream)) + is = new BufferedInputStream(is); + + headers = new InternetHeaders(is); + + if (is instanceof SharedInputStream) { + SharedInputStream sis = (SharedInputStream)is; + contentStream = sis.newStream(sis.getPosition(), -1); + } else { + try { + content = ASCIIUtility.getBytes(is); + } catch (IOException ioex) { + throw new MessagingException("Error reading input stream", ioex); + } + } + + } + + /** + * Constructs a MimeBodyPart using the given header and + * content bytes.

    + * + * Used by providers. + * + * @param headers The header of this part + * @param content bytes representing the body of this part. + * @exception MessagingException for failures + */ + public MimeBodyPart(InternetHeaders headers, byte[] content) + throws MessagingException { + super(); + this.headers = headers; + this.content = content; + } + + /** + * Return the size of the content of this body part in bytes. + * Return -1 if the size cannot be determined.

    + * + * Note that this number may not be an exact measure of the + * content size and may or may not account for any transfer + * encoding of the content.

    + * + * This implementation returns the size of the content + * array (if not null), or, if contentStream is not + * null, and the available method returns a positive + * number, it returns that number as the size. Otherwise, it returns + * -1. + * + * @return size in bytes, or -1 if not known + */ + @Override + public int getSize() throws MessagingException { + if (content != null) + return content.length; + if (contentStream != null) { + try { + int size = contentStream.available(); + // only believe the size if it's greate than zero, since zero + // is the default returned by the InputStream class itself + if (size > 0) + return size; + } catch (IOException ex) { + // ignore it + } + } + return -1; + } + + /** + * Return the number of lines for the content of this Part. + * Return -1 if this number cannot be determined.

    + * + * Note that this number may not be an exact measure of the + * content length and may or may not account for any transfer + * encoding of the content.

    + * + * This implementation returns -1. + * + * @return number of lines, or -1 if not known + */ + @Override + public int getLineCount() throws MessagingException { + return -1; + } + + /** + * Returns the value of the RFC 822 "Content-Type" header field. + * This represents the content type of the content of this + * body part. This value must not be null. If this field is + * unavailable, "text/plain" should be returned.

    + * + * This implementation uses getHeader(name) + * to obtain the requisite header field. + * + * @return Content-Type of this body part + */ + @Override + public String getContentType() throws MessagingException { + String s = getHeader("Content-Type", null); + s = MimeUtil.cleanContentType(this, s); + if (s == null) + s = "text/plain"; + return s; + } + + /** + * Is this Part of the specified MIME type? This method + * compares only the primaryType and + * subType. + * The parameters of the content types are ignored.

    + * + * For example, this method will return true when + * comparing a Part of content type "text/plain" + * with "text/plain; charset=foobar".

    + * + * If the subType of mimeType is the + * special character '*', then the subtype is ignored during the + * comparison. + * + * @exception MessagingException for failures + */ + @Override + public boolean isMimeType(String mimeType) throws MessagingException { + return isMimeType(this, mimeType); + } + + /** + * Returns the disposition from the "Content-Disposition" header field. + * This represents the disposition of this part. The disposition + * describes how the part should be presented to the user.

    + * + * If the Content-Disposition field is unavailable, + * null is returned.

    + * + * This implementation uses getHeader(name) + * to obtain the requisite header field. + * + * @exception MessagingException for failures + * @see #headers + */ + @Override + public String getDisposition() throws MessagingException { + return getDisposition(this); + } + + /** + * Set the disposition in the "Content-Disposition" header field + * of this body part. If the disposition is null, any existing + * "Content-Disposition" header field is removed. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this body part is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setDisposition(String disposition) throws MessagingException { + setDisposition(this, disposition); + } + + /** + * Returns the content transfer encoding from the + * "Content-Transfer-Encoding" header + * field. Returns null if the header is unavailable + * or its value is absent.

    + * + * This implementation uses getHeader(name) + * to obtain the requisite header field. + * + * @see #headers + */ + @Override + public String getEncoding() throws MessagingException { + return getEncoding(this); + } + + /** + * Returns the value of the "Content-ID" header field. Returns + * null if the field is unavailable or its value is + * absent.

    + * + * This implementation uses getHeader(name) + * to obtain the requisite header field. + */ + @Override + public String getContentID() throws MessagingException { + return getHeader("Content-Id", null); + } + + /** + * Set the "Content-ID" header field of this body part. + * If the cid parameter is null, any existing + * "Content-ID" is removed. + * + * @param cid the Content-ID + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this body part is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + * @since JavaMail 1.3 + */ + public void setContentID(String cid) throws MessagingException { + if (cid == null) + removeHeader("Content-ID"); + else + setHeader("Content-ID", cid); + } + + /** + * Return the value of the "Content-MD5" header field. Returns + * null if this field is unavailable or its value + * is absent.

    + * + * This implementation uses getHeader(name) + * to obtain the requisite header field. + */ + @Override + public String getContentMD5() throws MessagingException { + return getHeader("Content-MD5", null); + } + + /** + * Set the "Content-MD5" header field of this body part. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this body part is + * obtained from a READ_ONLY folder. + */ + @Override + public void setContentMD5(String md5) throws MessagingException { + setHeader("Content-MD5", md5); + } + + /** + * Get the languages specified in the Content-Language header + * of this MimePart. The Content-Language header is defined by + * RFC 1766. Returns null if this header is not + * available or its value is absent.

    + * + * This implementation uses getHeader(name) + * to obtain the requisite header field. + */ + @Override + public String[] getContentLanguage() throws MessagingException { + return getContentLanguage(this); + } + + /** + * Set the Content-Language header of this MimePart. The + * Content-Language header is defined by RFC 1766. + * + * @param languages array of language tags + */ + @Override + public void setContentLanguage(String[] languages) + throws MessagingException { + setContentLanguage(this, languages); + } + + /** + * Returns the "Content-Description" header field of this body part. + * This typically associates some descriptive information with + * this part. Returns null if this field is unavailable or its + * value is absent.

    + * + * If the Content-Description field is encoded as per RFC 2047, + * it is decoded and converted into Unicode. If the decoding or + * conversion fails, the raw data is returned as is.

    + * + * This implementation uses getHeader(name) + * to obtain the requisite header field. + * + * @return content description + */ + @Override + public String getDescription() throws MessagingException { + return getDescription(this); + } + + /** + * Set the "Content-Description" header field for this body part. + * If the description parameter is null, then any + * existing "Content-Description" fields are removed.

    + * + * If the description contains non US-ASCII characters, it will + * be encoded using the platform's default charset. If the + * description contains only US-ASCII characters, no encoding + * is done and it is used as is.

    + * + * Note that if the charset encoding process fails, a + * MessagingException is thrown, and an UnsupportedEncodingException + * is included in the chain of nested exceptions within the + * MessagingException. + * + * @param description content description + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this body part is + * obtained from a READ_ONLY folder. + * @exception MessagingException otherwise; an + * UnsupportedEncodingException may be included + * in the exception chain if the charset + * conversion fails. + */ + @Override + public void setDescription(String description) throws MessagingException { + setDescription(description, null); + } + + /** + * Set the "Content-Description" header field for this body part. + * If the description parameter is null, then any + * existing "Content-Description" fields are removed.

    + * + * If the description contains non US-ASCII characters, it will + * be encoded using the specified charset. If the description + * contains only US-ASCII characters, no encoding is done and + * it is used as is.

    + * + * Note that if the charset encoding process fails, a + * MessagingException is thrown, and an UnsupportedEncodingException + * is included in the chain of nested exceptions within the + * MessagingException. + * + * @param description Description + * @param charset Charset for encoding + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this body part is + * obtained from a READ_ONLY folder. + * @exception MessagingException otherwise; an + * UnsupportedEncodingException may be included + * in the exception chain if the charset + * conversion fails. + */ + public void setDescription(String description, String charset) + throws MessagingException { + setDescription(this, description, charset); + } + + /** + * Get the filename associated with this body part.

    + * + * Returns the value of the "filename" parameter from the + * "Content-Disposition" header field of this body part. If its + * not available, returns the value of the "name" parameter from + * the "Content-Type" header field of this body part. + * Returns null if both are absent.

    + * + * If the mail.mime.decodefilename System property + * is set to true, the {@link MimeUtility#decodeText + * MimeUtility.decodeText} method will be used to decode the + * filename. While such encoding is not supported by the MIME + * spec, many mailers use this technique to support non-ASCII + * characters in filenames. The default value of this property + * is false. + * + * @return filename + */ + @Override + public String getFileName() throws MessagingException { + return getFileName(this); + } + + /** + * Set the filename associated with this body part, if possible.

    + * + * Sets the "filename" parameter of the "Content-Disposition" + * header field of this body part. For compatibility with older + * mailers, the "name" parameter of the "Content-Type" header is + * also set.

    + * + * If the mail.mime.encodefilename System property + * is set to true, the {@link MimeUtility#encodeText + * MimeUtility.encodeText} method will be used to encode the + * filename. While such encoding is not supported by the MIME + * spec, many mailers use this technique to support non-ASCII + * characters in filenames. The default value of this property + * is false. + * + * @param filename the file name + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this body part is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setFileName(String filename) throws MessagingException { + setFileName(this, filename); + } + + /** + * Return a decoded input stream for this body part's "content".

    + * + * This implementation obtains the input stream from the DataHandler. + * That is, it invokes getDataHandler().getInputStream(); + * + * @return an InputStream + * @exception IOException this is typically thrown by the + * DataHandler. Refer to the documentation for + * javax.activation.DataHandler for more details. + * @exception MessagingException for other failures + * + * @see #getContentStream + * @see javax.activation.DataHandler#getInputStream + */ + @Override + public InputStream getInputStream() + throws IOException, MessagingException { + return getDataHandler().getInputStream(); + } + + /** + * Produce the raw bytes of the content. This method is used + * when creating a DataHandler object for the content. Subclasses + * that can provide a separate input stream for just the Part + * content might want to override this method.

    + * + * @return an InputStream containing the raw bytes + * @exception MessagingException for failures + * @see #content + * @see MimeMessage#getContentStream + */ + protected InputStream getContentStream() throws MessagingException { + if (contentStream != null) + return ((SharedInputStream)contentStream).newStream(0, -1); + if (content != null) + return new ByteArrayInputStream(content); + + throw new MessagingException("No MimeBodyPart content"); + } + + /** + * Return an InputStream to the raw data with any Content-Transfer-Encoding + * intact. This method is useful if the "Content-Transfer-Encoding" + * header is incorrect or corrupt, which would prevent the + * getInputStream method or getContent method + * from returning the correct data. In such a case the application may + * use this method and attempt to decode the raw data itself.

    + * + * This implementation simply calls the getContentStream + * method. + * + * @return an InputStream containing the raw bytes + * @exception MessagingException for failures + * @see #getInputStream + * @see #getContentStream + * @since JavaMail 1.2 + */ + public InputStream getRawInputStream() throws MessagingException { + return getContentStream(); + } + + /** + * Return a DataHandler for this body part's content.

    + * + * The implementation provided here works just like the + * the implementation in MimeMessage. + * @see MimeMessage#getDataHandler + */ + @Override + public DataHandler getDataHandler() throws MessagingException { + if (dh == null) + dh = new MimePartDataHandler(this); + return dh; + } + + /** + * Return the content as a Java object. The type of the object + * returned is of course dependent on the content itself. For + * example, the native format of a text/plain content is usually + * a String object. The native format for a "multipart" + * content is always a Multipart subclass. For content types that are + * unknown to the DataHandler system, an input stream is returned + * as the content.

    + * + * This implementation obtains the content from the DataHandler. + * That is, it invokes getDataHandler().getContent(); + * If the content is a Multipart or Message object and was created by + * parsing a stream, the object is cached and returned in subsequent + * calls so that modifications to the content will not be lost. + * + * @return Object + * @exception IOException this is typically thrown by the + * DataHandler. Refer to the documentation for + * javax.activation.DataHandler for more details. + * @exception MessagingException for other failures + */ + @Override + public Object getContent() throws IOException, MessagingException { + if (cachedContent != null) + return cachedContent; + Object c; + try { + c = getDataHandler().getContent(); + } catch (FolderClosedIOException fex) { + throw new FolderClosedException(fex.getFolder(), fex.getMessage()); + } catch (MessageRemovedIOException mex) { + throw new MessageRemovedException(mex.getMessage()); + } + if (cacheMultipart && + (c instanceof Multipart || c instanceof Message) && + (content != null || contentStream != null)) { + cachedContent = c; + /* + * We may abandon the input stream so make sure + * the MimeMultipart has consumed the stream. + */ + if (c instanceof MimeMultipart) + ((MimeMultipart)c).parse(); + } + return c; + } + + /** + * This method provides the mechanism to set this body part's content. + * The given DataHandler object should wrap the actual content. + * + * @param dh The DataHandler for the content + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this body part is + * obtained from a READ_ONLY folder. + */ + @Override + public void setDataHandler(DataHandler dh) + throws MessagingException { + this.dh = dh; + cachedContent = null; + MimeBodyPart.invalidateContentHeaders(this); + } + + /** + * A convenience method for setting this body part's content.

    + * + * The content is wrapped in a DataHandler object. Note that a + * DataContentHandler class for the specified type should be + * available to the JavaMail implementation for this to work right. + * That is, to do setContent(foobar, "application/x-foobar"), + * a DataContentHandler for "application/x-foobar" should be installed. + * Refer to the Java Activation Framework for more information. + * + * @param o the content object + * @param type Mime type of the object + * @exception IllegalWriteException if the underlying + * implementation does not support modification of + * existing values + * @exception IllegalStateException if this body part is + * obtained from a READ_ONLY folder. + */ + @Override + public void setContent(Object o, String type) + throws MessagingException { + if (o instanceof Multipart) { + setContent((Multipart)o); + } else { + setDataHandler(new DataHandler(o, type)); + } + } + + /** + * Convenience method that sets the given String as this + * part's content, with a MIME type of "text/plain". If the + * string contains non US-ASCII characters, it will be encoded + * using the platform's default charset. The charset is also + * used to set the "charset" parameter.

    + * + * Note that there may be a performance penalty if + * text is large, since this method may have + * to scan all the characters to determine what charset to + * use.

    + * + * If the charset is already known, use the + * setText method that takes the charset parameter. + * + * @param text the text content to set + * @exception MessagingException if an error occurs + * @see #setText(String text, String charset) + */ + @Override + public void setText(String text) throws MessagingException { + setText(text, null); + } + + /** + * Convenience method that sets the given String as this part's + * content, with a MIME type of "text/plain" and the specified + * charset. The given Unicode string will be charset-encoded + * using the specified charset. The charset is also used to set + * the "charset" parameter. + * + * @param text the text content to set + * @param charset the charset to use for the text + * @exception MessagingException if an error occurs + */ + @Override + public void setText(String text, String charset) + throws MessagingException { + setText(this, text, charset, "plain"); + } + + /** + * Convenience method that sets the given String as this part's + * content, with a primary MIME type of "text" and the specified + * MIME subtype. The given Unicode string will be charset-encoded + * using the specified charset. The charset is also used to set + * the "charset" parameter. + * + * @param text the text content to set + * @param charset the charset to use for the text + * @param subtype the MIME subtype to use (e.g., "html") + * @exception MessagingException if an error occurs + * @since JavaMail 1.4 + */ + @Override + public void setText(String text, String charset, String subtype) + throws MessagingException { + setText(this, text, charset, subtype); + } + + /** + * This method sets the body part's content to a Multipart object. + * + * @param mp The multipart object that is the Message's content + * @exception IllegalWriteException if the underlying + * implementation does not support modification of + * existing values. + * @exception IllegalStateException if this body part is + * obtained from a READ_ONLY folder. + */ + @Override + public void setContent(Multipart mp) throws MessagingException { + setDataHandler(new DataHandler(mp, mp.getContentType())); + mp.setParent(this); + } + + /** + * Use the specified file to provide the data for this part. + * The simple file name is used as the file name for this + * part and the data in the file is used as the data for this + * part. The encoding will be chosen appropriately for the + * file data. The disposition of this part is set to + * {@link Part#ATTACHMENT Part.ATTACHMENT}. + * + * @param file the File object to attach + * @exception IOException errors related to accessing the file + * @exception MessagingException message related errors + * @since JavaMail 1.4 + */ + public void attachFile(File file) throws IOException, MessagingException { + FileDataSource fds = new FileDataSource(file); + this.setDataHandler(new DataHandler(fds)); + this.setFileName(fds.getName()); + this.setDisposition(ATTACHMENT); + } + + /** + * Use the specified file to provide the data for this part. + * The simple file name is used as the file name for this + * part and the data in the file is used as the data for this + * part. The encoding will be chosen appropriately for the + * file data. + * + * @param file the name of the file to attach + * @exception IOException errors related to accessing the file + * @exception MessagingException message related errors + * @since JavaMail 1.4 + */ + public void attachFile(String file) throws IOException, MessagingException { + File f = new File(file); + attachFile(f); + } + + /** + * Use the specified file with the specified Content-Type and + * Content-Transfer-Encoding to provide the data for this part. + * If contentType or encoding are null, appropriate values will + * be chosen. + * The simple file name is used as the file name for this + * part and the data in the file is used as the data for this + * part. The disposition of this part is set to + * {@link Part#ATTACHMENT Part.ATTACHMENT}. + * + * @param file the File object to attach + * @param contentType the Content-Type, or null + * @param encoding the Content-Transfer-Encoding, or null + * @exception IOException errors related to accessing the file + * @exception MessagingException message related errors + * @since JavaMail 1.5 + */ + public void attachFile(File file, String contentType, String encoding) + throws IOException, MessagingException { + DataSource fds = new EncodedFileDataSource(file, contentType, encoding); + this.setDataHandler(new DataHandler(fds)); + this.setFileName(fds.getName()); + this.setDisposition(ATTACHMENT); + } + + /** + * Use the specified file with the specified Content-Type and + * Content-Transfer-Encoding to provide the data for this part. + * If contentType or encoding are null, appropriate values will + * be chosen. + * The simple file name is used as the file name for this + * part and the data in the file is used as the data for this + * part. The disposition of this part is set to + * {@link Part#ATTACHMENT Part.ATTACHMENT}. + * + * @param file the name of the file + * @param contentType the Content-Type, or null + * @param encoding the Content-Transfer-Encoding, or null + * @exception IOException errors related to accessing the file + * @exception MessagingException message related errors + * @since JavaMail 1.5 + */ + public void attachFile(String file, String contentType, String encoding) + throws IOException, MessagingException { + attachFile(new File(file), contentType, encoding); + } + + /** + * A FileDataSource class that allows us to specify the + * Content-Type and Content-Transfer-Encoding. + */ + private static class EncodedFileDataSource extends FileDataSource + implements EncodingAware { + private String contentType; + private String encoding; + + public EncodedFileDataSource(File file, String contentType, + String encoding) { + super(file); + this.contentType = contentType; + this.encoding = encoding; + } + + // overrides DataSource.getContentType() + @Override + public String getContentType() { + return contentType != null ? contentType : super.getContentType(); + } + + // implements EncodingAware.getEncoding() + @Override + public String getEncoding() { + return encoding; + } + } + + /** + * Save the contents of this part in the specified file. The content + * is decoded and saved, without any of the MIME headers. + * + * @param file the File object to write to + * @exception IOException errors related to accessing the file + * @exception MessagingException message related errors + * @since JavaMail 1.4 + */ + public void saveFile(File file) throws IOException, MessagingException { + OutputStream out = null; + InputStream in = null; + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + in = this.getInputStream(); + byte[] buf = new byte[8192]; + int len; + while ((len = in.read(buf)) > 0) + out.write(buf, 0, len); + } finally { + // close streams, but don't mask original exception, if any + try { + if (in != null) + in.close(); + } catch (IOException ex) { } + try { + if (out != null) + out.close(); + } catch (IOException ex) { } + } + } + + /** + * Save the contents of this part in the specified file. The content + * is decoded and saved, without any of the MIME headers. + * + * @param file the name of the file to write to + * @exception IOException errors related to accessing the file + * @exception MessagingException message related errors + * @since JavaMail 1.4 + */ + public void saveFile(String file) throws IOException, MessagingException { + File f = new File(file); + saveFile(f); + } + + /** + * Output the body part as an RFC 822 format stream. + * + * @exception IOException if an error occurs writing to the + * stream or if an error is generated + * by the javax.activation layer. + * @exception MessagingException for other failures + * @see javax.activation.DataHandler#writeTo + */ + @Override + public void writeTo(OutputStream os) + throws IOException, MessagingException { + writeTo(this, os, null); + } + + /** + * Get all the headers for this header_name. Note that certain + * headers may be encoded as per RFC 2047 if they contain + * non US-ASCII characters and these should be decoded. + * + * @param name name of header + * @return array of headers + * @see javax.mail.internet.MimeUtility + */ + @Override + public String[] getHeader(String name) throws MessagingException { + return headers.getHeader(name); + } + + /** + * Get all the headers for this header name, returned as a single + * String, with headers separated by the delimiter. If the + * delimiter is null, only the first header is + * returned. + * + * @param name the name of this header + * @param delimiter delimiter between fields in returned string + * @return the value fields for all headers with + * this name + * @exception MessagingException for failures + */ + @Override + public String getHeader(String name, String delimiter) + throws MessagingException { + return headers.getHeader(name, delimiter); + } + + /** + * Set the value for this header_name. Replaces all existing + * header values with this new value. Note that RFC 822 headers + * must contain only US-ASCII characters, so a header that + * contains non US-ASCII characters must be encoded as per the + * rules of RFC 2047. + * + * @param name header name + * @param value header value + * @see javax.mail.internet.MimeUtility + */ + @Override + public void setHeader(String name, String value) + throws MessagingException { + headers.setHeader(name, value); + } + + /** + * Add this value to the existing values for this header_name. + * Note that RFC 822 headers must contain only US-ASCII + * characters, so a header that contains non US-ASCII characters + * must be encoded as per the rules of RFC 2047. + * + * @param name header name + * @param value header value + * @see javax.mail.internet.MimeUtility + */ + @Override + public void addHeader(String name, String value) + throws MessagingException { + headers.addHeader(name, value); + } + + /** + * Remove all headers with this name. + */ + @Override + public void removeHeader(String name) throws MessagingException { + headers.removeHeader(name); + } + + /** + * Return all the headers from this Message as an Enumeration of + * Header objects. + */ + @Override + public Enumeration

    getAllHeaders() throws MessagingException { + return headers.getAllHeaders(); + } + + /** + * Return matching headers from this Message as an Enumeration of + * Header objects. + */ + @Override + public Enumeration
    getMatchingHeaders(String[] names) + throws MessagingException { + return headers.getMatchingHeaders(names); + } + + /** + * Return non-matching headers from this Message as an + * Enumeration of Header objects. + */ + @Override + public Enumeration
    getNonMatchingHeaders(String[] names) + throws MessagingException { + return headers.getNonMatchingHeaders(names); + } + + /** + * Add a header line to this body part + */ + @Override + public void addHeaderLine(String line) throws MessagingException { + headers.addHeaderLine(line); + } + + /** + * Get all header lines as an Enumeration of Strings. A Header + * line is a raw RFC 822 header line, containing both the "name" + * and "value" field. + */ + @Override + public Enumeration getAllHeaderLines() throws MessagingException { + return headers.getAllHeaderLines(); + } + + /** + * Get matching header lines as an Enumeration of Strings. + * A Header line is a raw RFC 822 header line, containing both + * the "name" and "value" field. + */ + @Override + public Enumeration getMatchingHeaderLines(String[] names) + throws MessagingException { + return headers.getMatchingHeaderLines(names); + } + + /** + * Get non-matching header lines as an Enumeration of Strings. + * A Header line is a raw RFC 822 header line, containing both + * the "name" and "value" field. + */ + @Override + public Enumeration getNonMatchingHeaderLines(String[] names) + throws MessagingException { + return headers.getNonMatchingHeaderLines(names); + } + + /** + * Examine the content of this body part and update the appropriate + * MIME headers. Typical headers that get set here are + * Content-Type and Content-Transfer-Encoding. + * Headers might need to be updated in two cases: + * + *
    + * - A message being crafted by a mail application will certainly + * need to activate this method at some point to fill up its internal + * headers. + * + *
    + * - A message read in from a Store will have obtained + * all its headers from the store, and so doesn't need this. + * However, if this message is editable and if any edits have + * been made to either the content or message structure, we might + * need to resync our headers. + * + *
    + * In both cases this method is typically called by the + * Message.saveChanges method.

    + * + * If the {@link #cachedContent} field is not null (that is, + * it references a Multipart or Message object), then + * that object is used to set a new DataHandler, any + * stream data used to create this object is discarded, + * and the {@link #cachedContent} field is cleared. + * + * @exception MessagingException for failures + */ + protected void updateHeaders() throws MessagingException { + updateHeaders(this); + /* + * If we've cached a Multipart or Message object then + * we're now committed to using this instance of the + * object and we discard any stream data used to create + * this object. + */ + if (cachedContent != null) { + dh = new DataHandler(cachedContent, getContentType()); + cachedContent = null; + content = null; + if (contentStream != null) { + try { + contentStream.close(); + } catch (IOException ioex) { } // nothing to do + } + contentStream = null; + } + } + + ///////////////////////////////////////////////////////////// + // Package private convenience methods to share code among // + // MimeMessage and MimeBodyPart // + ///////////////////////////////////////////////////////////// + + static boolean isMimeType(MimePart part, String mimeType) + throws MessagingException { + // XXX - lots of room for optimization here! + String type = part.getContentType(); + try { + return new ContentType(type).match(mimeType); + } catch (ParseException ex) { + // we only need the type and subtype so throw away the rest + try { + int i = type.indexOf(';'); + if (i > 0) + return new ContentType(type.substring(0, i)).match(mimeType); + } catch (ParseException pex2) { + } + return type.equalsIgnoreCase(mimeType); + } + } + + static void setText(MimePart part, String text, String charset, + String subtype) throws MessagingException { + if (charset == null) { + if (MimeUtility.checkAscii(text) != MimeUtility.ALL_ASCII) + charset = MimeUtility.getDefaultMIMECharset(); + else + charset = "us-ascii"; + } + // XXX - should at least ensure that subtype is an atom + part.setContent(text, "text/" + subtype + "; charset=" + + MimeUtility.quote(charset, HeaderTokenizer.MIME)); + } + + static String getDisposition(MimePart part) throws MessagingException { + String s = part.getHeader("Content-Disposition", null); + + if (s == null) + return null; + + ContentDisposition cd = new ContentDisposition(s); + return cd.getDisposition(); + } + + static void setDisposition(MimePart part, String disposition) + throws MessagingException { + if (disposition == null) + part.removeHeader("Content-Disposition"); + else { + String s = part.getHeader("Content-Disposition", null); + if (s != null) { + /* A Content-Disposition header already exists .. + * + * Override disposition, but attempt to retain + * existing disposition parameters + */ + ContentDisposition cd = new ContentDisposition(s); + cd.setDisposition(disposition); + disposition = cd.toString(); + } + part.setHeader("Content-Disposition", disposition); + } + } + + static String getDescription(MimePart part) + throws MessagingException { + String rawvalue = part.getHeader("Content-Description", null); + + if (rawvalue == null) + return null; + + try { + return MimeUtility.decodeText(MimeUtility.unfold(rawvalue)); + } catch (UnsupportedEncodingException ex) { + return rawvalue; + } + } + + static void + setDescription(MimePart part, String description, String charset) + throws MessagingException { + if (description == null) { + part.removeHeader("Content-Description"); + return; + } + + try { + part.setHeader("Content-Description", MimeUtility.fold(21, + MimeUtility.encodeText(description, charset, null))); + } catch (UnsupportedEncodingException uex) { + throw new MessagingException("Encoding error", uex); + } + } + + static String getFileName(MimePart part) throws MessagingException { + String filename = null; + String s = part.getHeader("Content-Disposition", null); + + if (s != null) { + // Parse the header .. + ContentDisposition cd = new ContentDisposition(s); + filename = cd.getParameter("filename"); + } + if (filename == null) { + // Still no filename ? Try the "name" ContentType parameter + s = part.getHeader("Content-Type", null); + s = MimeUtil.cleanContentType(part, s); + if (s != null) { + try { + ContentType ct = new ContentType(s); + filename = ct.getParameter("name"); + } catch (ParseException pex) { } // ignore it + } + } + if (decodeFileName && filename != null) { + try { + filename = MimeUtility.decodeText(filename); + } catch (UnsupportedEncodingException ex) { + throw new MessagingException("Can't decode filename", ex); + } + } + return filename; + } + + static void setFileName(MimePart part, String name) + throws MessagingException { + if (encodeFileName && name != null) { + try { + name = MimeUtility.encodeText(name); + } catch (UnsupportedEncodingException ex) { + throw new MessagingException("Can't encode filename", ex); + } + } + + // Set the Content-Disposition "filename" parameter + String s = part.getHeader("Content-Disposition", null); + ContentDisposition cd = + new ContentDisposition(s == null ? Part.ATTACHMENT : s); + // ensure that the filename is encoded if necessary + String charset = MimeUtility.getDefaultMIMECharset(); + ParameterList p = cd.getParameterList(); + if (p == null) { + p = new ParameterList(); + cd.setParameterList(p); + } + if (encodeFileName) + p.setLiteral("filename", name); + else + p.set("filename", name, charset); + part.setHeader("Content-Disposition", cd.toString()); + + /* + * Also attempt to set the Content-Type "name" parameter, + * to satisfy ancient MUAs. XXX - This is not RFC compliant. + */ + if (setContentTypeFileName) { + s = part.getHeader("Content-Type", null); + s = MimeUtil.cleanContentType(part, s); + if (s != null) { + try { + ContentType cType = new ContentType(s); + // ensure that the filename is encoded if necessary + p = cType.getParameterList(); + if (p == null) { + p = new ParameterList(); + cType.setParameterList(p); + } + if (encodeFileName) + p.setLiteral("name", name); + else + p.set("name", name, charset); + part.setHeader("Content-Type", cType.toString()); + } catch (ParseException pex) { } // ignore it + } + } + } + + static String[] getContentLanguage(MimePart part) + throws MessagingException { + String s = part.getHeader("Content-Language", null); + + if (s == null) + return null; + + // Tokenize the header to obtain the Language-tags (skip comments) + HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME); + List v = new ArrayList<>(); + + HeaderTokenizer.Token tk; + int tkType; + + while (true) { + tk = h.next(); // get a language-tag + tkType = tk.getType(); + if (tkType == HeaderTokenizer.Token.EOF) + break; // done + else if (tkType == HeaderTokenizer.Token.ATOM) + v.add(tk.getValue()); + else // invalid token, skip it. + continue; + } + + if (v.isEmpty()) + return null; + + String[] language = new String[v.size()]; + v.toArray(language); + return language; + } + + static void setContentLanguage(MimePart part, String[] languages) + throws MessagingException { + StringBuilder sb = new StringBuilder(languages[0]); + int len = "Content-Language".length() + 2 + languages[0].length(); + for (int i = 1; i < languages.length; i++) { + sb.append(','); + len++; + if (len > 76) { + sb.append("\r\n\t"); + len = 8; + } + sb.append(languages[i]); + len += languages[i].length(); + } + part.setHeader("Content-Language", sb.toString()); + } + + static String getEncoding(MimePart part) throws MessagingException { + String s = part.getHeader("Content-Transfer-Encoding", null); + + if (s == null) + return null; + + s = s.trim(); // get rid of trailing spaces + if (s.length() == 0) + return null; + // quick check for known values to avoid unnecessary use + // of tokenizer. + if (s.equalsIgnoreCase("7bit") || s.equalsIgnoreCase("8bit") || + s.equalsIgnoreCase("quoted-printable") || + s.equalsIgnoreCase("binary") || + s.equalsIgnoreCase("base64")) + return s; + + // Tokenize the header to obtain the encoding (skip comments) + HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME); + + HeaderTokenizer.Token tk; + int tkType; + + for (;;) { + tk = h.next(); // get a token + tkType = tk.getType(); + if (tkType == HeaderTokenizer.Token.EOF) + break; // done + else if (tkType == HeaderTokenizer.Token.ATOM) + return tk.getValue(); + else // invalid token, skip it. + continue; + } + return s; + } + + static void setEncoding(MimePart part, String encoding) + throws MessagingException { + part.setHeader("Content-Transfer-Encoding", encoding); + } + + /** + * Restrict the encoding to values allowed for the + * Content-Type of the specified MimePart. Returns + * either the original encoding or null. + */ + static String restrictEncoding(MimePart part, String encoding) + throws MessagingException { + if (!ignoreMultipartEncoding || encoding == null) + return encoding; + + if (encoding.equalsIgnoreCase("7bit") || + encoding.equalsIgnoreCase("8bit") || + encoding.equalsIgnoreCase("binary")) + return encoding; // these encodings are always valid + + String type = part.getContentType(); + if (type == null) + return encoding; + + try { + /* + * multipart and message types aren't allowed to have + * encodings except for the three mentioned above. + * If it's one of these types, ignore the encoding. + */ + ContentType cType = new ContentType(type); + if (cType.match("multipart/*")) + return null; + if (cType.match("message/*") && + !PropUtil.getBooleanSystemProperty( + "mail.mime.allowencodedmessages", false)) + return null; + } catch (ParseException pex) { + // ignore it + } + return encoding; + } + + static void updateHeaders(MimePart part) throws MessagingException { + DataHandler dh = part.getDataHandler(); + if (dh == null) // Huh ? + return; + + try { + String type = dh.getContentType(); + boolean composite = false; + boolean needCTHeader = part.getHeader("Content-Type") == null; + + ContentType cType = new ContentType(type); + + /* + * If this is a multipart, give sub-parts a chance to + * update their headers. Even though the data for this + * multipart may have come from a stream, one of the + * sub-parts may have been updated. + */ + if (cType.match("multipart/*")) { + // If multipart, recurse + composite = true; + Object o; + if (part instanceof MimeBodyPart) { + MimeBodyPart mbp = (MimeBodyPart)part; + o = mbp.cachedContent != null ? + mbp.cachedContent : dh.getContent(); + } else if (part instanceof MimeMessage) { + MimeMessage msg = (MimeMessage)part; + o = msg.cachedContent != null ? + msg.cachedContent : dh.getContent(); + } else + o = dh.getContent(); + if (o instanceof MimeMultipart) + ((MimeMultipart)o).updateHeaders(); + else + throw new MessagingException("MIME part of type \"" + + type + "\" contains object of type " + + o.getClass().getName() + " instead of MimeMultipart"); + } else if (cType.match("message/rfc822")) { + composite = true; + // XXX - call MimeMessage.updateHeaders()? + } + + /* + * If this is our own MimePartDataHandler, we can't update any + * of the headers. + * + * If this is a MimePartDataHandler coming from another part, + * we need to copy over the content headers from the other part. + * Note that the MimePartDataHandler still refers to the original + * data and the original MimePart. + */ + if (dh instanceof MimePartDataHandler) { + MimePartDataHandler mdh = (MimePartDataHandler)dh; + MimePart mpart = mdh.getPart(); + if (mpart != part) { + if (needCTHeader) + part.setHeader("Content-Type", mpart.getContentType()); + // XXX - can't change the encoding of the data from the + // other part without decoding and reencoding it, so + // we just force it to match the original, but if the + // original has no encoding we'll consider reencoding it + String enc = mpart.getEncoding(); + if (enc != null) { + setEncoding(part, enc); + return; + } + } else + return; + } + + // Content-Transfer-Encoding, but only if we don't + // already have one + if (!composite) { // not allowed on composite parts + if (part.getHeader("Content-Transfer-Encoding") == null) + setEncoding(part, MimeUtility.getEncoding(dh)); + + if (needCTHeader && setDefaultTextCharset && + cType.match("text/*") && + cType.getParameter("charset") == null) { + /* + * Set a default charset for text parts. + * We really should examine the data to determine + * whether or not it's all ASCII, but that's too + * expensive so we make an assumption: If we + * chose 7bit encoding for this data, it's probably + * ASCII. (MimeUtility.getEncoding will choose + * 7bit only in this case, but someone might've + * set the Content-Transfer-Encoding header manually.) + */ + String charset; + String enc = part.getEncoding(); + if (enc != null && enc.equalsIgnoreCase("7bit")) + charset = "us-ascii"; + else + charset = MimeUtility.getDefaultMIMECharset(); + cType.setParameter("charset", charset); + type = cType.toString(); + } + } + + // Now, let's update our own headers ... + + // Content-type, but only if we don't already have one + if (needCTHeader) { + /* + * Pull out "filename" from Content-Disposition, and + * use that to set the "name" parameter. This is to + * satisfy older MUAs (DtMail, Roam and probably + * a bunch of others). + */ + if (setContentTypeFileName) { + String s = part.getHeader("Content-Disposition", null); + if (s != null) { + // Parse the header .. + ContentDisposition cd = new ContentDisposition(s); + String filename = cd.getParameter("filename"); + if (filename != null) { + ParameterList p = cType.getParameterList(); + if (p == null) { + p = new ParameterList(); + cType.setParameterList(p); + } + if (encodeFileName) + p.setLiteral("name", + MimeUtility.encodeText(filename)); + else + p.set("name", filename, + MimeUtility.getDefaultMIMECharset()); + type = cType.toString(); + } + } + } + + part.setHeader("Content-Type", type); + } + } catch (IOException ex) { + throw new MessagingException("IOException updating headers", ex); + } + } + + static void invalidateContentHeaders(MimePart part) + throws MessagingException { + part.removeHeader("Content-Type"); + part.removeHeader("Content-Transfer-Encoding"); + } + + static void writeTo(MimePart part, OutputStream os, String[] ignoreList) + throws IOException, MessagingException { + + // see if we already have a LOS + LineOutputStream los = null; + if (os instanceof LineOutputStream) { + los = (LineOutputStream) os; + } else { + los = new LineOutputStream(os, allowutf8); + } + + // First, write out the header + Enumeration hdrLines + = part.getNonMatchingHeaderLines(ignoreList); + while (hdrLines.hasMoreElements()) + los.writeln(hdrLines.nextElement()); + + // The CRLF separator between header and content + los.writeln(); + + // Finally, the content. Encode if required. + // XXX: May need to account for ESMTP ? + InputStream is = null; + byte[] buf = null; + try { + /* + * If the data for this part comes from a stream, + * and is already encoded, + * just copy it to the output stream without decoding + * and reencoding it. + */ + DataHandler dh = part.getDataHandler(); + if (dh instanceof MimePartDataHandler) { + MimePartDataHandler mpdh = (MimePartDataHandler)dh; + MimePart mpart = mpdh.getPart(); + if (mpart.getEncoding() != null) + is = mpdh.getContentStream(); + } + if (is != null) { + // now copy the data to the output stream + buf = new byte[8192]; + int len; + while ((len = is.read(buf)) > 0) + os.write(buf, 0, len); + } else { + os = MimeUtility.encode(os, + restrictEncoding(part, part.getEncoding())); + part.getDataHandler().writeTo(os); + } + } finally { + if (is != null) + is.close(); + buf = null; + } + os.flush(); // Needed to complete encoding + } + + /** + * A special DataHandler used only as a marker to indicate that + * the source of the data is a MimePart (that is, a byte array + * or a stream). This prevents updateHeaders from trying to + * change the headers for such data. In particular, the original + * Content-Transfer-Encoding for the data must be preserved. + * Otherwise the data would need to be decoded and reencoded. + */ + static class MimePartDataHandler extends DataHandler { + MimePart part; + public MimePartDataHandler(MimePart part) { + super(new MimePartDataSource(part)); + this.part = part; + } + + InputStream getContentStream() throws MessagingException { + InputStream is = null; + + if (part instanceof MimeBodyPart) { + MimeBodyPart mbp = (MimeBodyPart)part; + is = mbp.getContentStream(); + } else if (part instanceof MimeMessage) { + MimeMessage msg = (MimeMessage)part; + is = msg.getContentStream(); + } + return is; + } + + MimePart getPart() { + return part; + } + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/MimeMessage.java b/fine-third-default/fine-mail/src/javax/mail/internet/MimeMessage.java new file mode 100644 index 000000000..46c952804 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/MimeMessage.java @@ -0,0 +1,2323 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import javax.mail.*; +import javax.activation.*; +import java.lang.*; +import java.io.*; +import java.util.*; +import java.text.ParseException; +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.util.MimeUtil; +import com.sun.mail.util.MessageRemovedIOException; +import com.sun.mail.util.FolderClosedIOException; +import com.sun.mail.util.LineOutputStream; +import javax.mail.util.SharedByteArrayInputStream; + +/** + * This class represents a MIME style email message. It implements + * the Message abstract class and the MimePart + * interface.

    + * + * Clients wanting to create new MIME style messages will instantiate + * an empty MimeMessage object and then fill it with appropriate + * attributes and content.

    + * + * Service providers that implement MIME compliant backend stores may + * want to subclass MimeMessage and override certain methods to provide + * specific implementations. The simplest case is probably a provider + * that generates a MIME style input stream and leaves the parsing of + * the stream to this class.

    + * + * MimeMessage uses the InternetHeaders class to parse and + * store the top level RFC 822 headers of a message.

    + * + * The mail.mime.address.strict session property controls + * the parsing of address headers. By default, strict parsing of address + * headers is done. If this property is set to "false", + * strict parsing is not done and many illegal addresses that sometimes + * occur in real messages are allowed. See the InternetAddress + * class for details. + * + *


    A note on RFC 822 and MIME headers

    + * + * RFC 822 header fields must contain only + * US-ASCII characters. MIME allows non ASCII characters to be present + * in certain portions of certain headers, by encoding those characters. + * RFC 2047 specifies the rules for doing this. The MimeUtility + * class provided in this package can be used to to achieve this. + * Callers of the setHeader, addHeader, and + * addHeaderLine methods are responsible for enforcing + * the MIME requirements for the specified headers. In addition, these + * header fields must be folded (wrapped) before being sent if they + * exceed the line length limitation for the transport (1000 bytes for + * SMTP). Received headers may have been folded. The application is + * responsible for folding and unfolding headers as appropriate.

    + * + * @author John Mani + * @author Bill Shannon + * @author Max Spivak + * @author Kanwar Oberoi + * @see javax.mail.internet.MimeUtility + * @see javax.mail.Part + * @see javax.mail.Message + * @see javax.mail.internet.MimePart + * @see javax.mail.internet.InternetAddress + */ + +public class MimeMessage extends Message implements MimePart { + + /** + * The DataHandler object representing this Message's content. + */ + protected DataHandler dh; + + /** + * Byte array that holds the bytes of this Message's content. + */ + protected byte[] content; + + /** + * If the data for this message was supplied by an + * InputStream that implements the SharedInputStream interface, + * contentStream is another such stream representing + * the content of this message. In this case, content + * will be null. + * + * @since JavaMail 1.2 + */ + protected InputStream contentStream; + + /** + * The InternetHeaders object that stores the header + * of this message. + */ + protected InternetHeaders headers; + + /** + * The Flags for this message. + */ + protected Flags flags; + + /** + * A flag indicating whether the message has been modified. + * If the message has not been modified, any data in the + * content array is assumed to be valid and is used + * directly in the writeTo method. This flag is + * set to true when an empty message is created or when the + * saveChanges method is called. + * + * @since JavaMail 1.2 + */ + protected boolean modified = false; + + /** + * Does the saveChanges method need to be called on + * this message? This flag is set to false by the public constructor + * and set to true by the saveChanges method. The + * writeTo method checks this flag and calls the + * saveChanges method as necessary. This avoids the + * common mistake of forgetting to call the saveChanges + * method on a newly constructed message. + * + * @since JavaMail 1.2 + */ + protected boolean saved = false; + + /** + * If our content is a Multipart or Message object, we save it + * the first time it's created by parsing a stream so that changes + * to the contained objects will not be lost.

    + * + * If this field is not null, it's return by the {@link #getContent} + * method. The {@link #getContent} method sets this field if it + * would return a Multipart or MimeMessage object. This field is + * is cleared by the {@link #setDataHandler} method. + * + * @since JavaMail 1.5 + */ + protected Object cachedContent; + + // Used to parse dates + private static final MailDateFormat mailDateFormat = new MailDateFormat(); + + // Should addresses in headers be parsed in "strict" mode? + private boolean strict = true; + // Is UTF-8 allowed in headers? + private boolean allowutf8 = false; + + /** + * Default constructor. An empty message object is created. + * The headers field is set to an empty InternetHeaders + * object. The flags field is set to an empty Flags + * object. The modified flag is set to true. + * + * @param session the Sesssion + */ + public MimeMessage(Session session) { + super(session); + modified = true; + headers = new InternetHeaders(); + flags = new Flags(); // empty flags object + initStrict(); + } + + /** + * Constructs a MimeMessage by reading and parsing the data from the + * specified MIME InputStream. The InputStream will be left positioned + * at the end of the data for the message. Note that the input stream + * parse is done within this constructor itself.

    + * + * The input stream contains an entire MIME formatted message with + * headers and data. + * + * @param session Session object for this message + * @param is the message input stream + * @exception MessagingException for failures + */ + public MimeMessage(Session session, InputStream is) + throws MessagingException { + super(session); + flags = new Flags(); // empty Flags object + initStrict(); + parse(is); + saved = true; + } + + /** + * Constructs a new MimeMessage with content initialized from the + * source MimeMessage. The new message is independent + * of the original.

    + * + * Note: The current implementation is rather inefficient, copying + * the data more times than strictly necessary. + * + * @param source the message to copy content from + * @exception MessagingException for failures + * @since JavaMail 1.2 + */ + public MimeMessage(MimeMessage source) throws MessagingException { + super(source.session); + flags = source.getFlags(); + if (flags == null) // make sure flags is always set + flags = new Flags(); + ByteArrayOutputStream bos; + int size = source.getSize(); + if (size > 0) + bos = new ByteArrayOutputStream(size); + else + bos = new ByteArrayOutputStream(); + try { + strict = source.strict; + source.writeTo(bos); + bos.close(); + SharedByteArrayInputStream bis = + new SharedByteArrayInputStream(bos.toByteArray()); + parse(bis); + bis.close(); + saved = true; + } catch (IOException ex) { + // should never happen, but just in case... + throw new MessagingException("IOException while copying message", + ex); + } + } + + /** + * Constructs an empty MimeMessage object with the given Folder + * and message number.

    + * + * This method is for providers subclassing MimeMessage. + * + * @param folder the Folder this message is from + * @param msgnum the number of this message + */ + protected MimeMessage(Folder folder, int msgnum) { + super(folder, msgnum); + flags = new Flags(); // empty Flags object + saved = true; + initStrict(); + } + + /** + * Constructs a MimeMessage by reading and parsing the data from the + * specified MIME InputStream. The InputStream will be left positioned + * at the end of the data for the message. Note that the input stream + * parse is done within this constructor itself.

    + * + * This method is for providers subclassing MimeMessage. + * + * @param folder The containing folder. + * @param is the message input stream + * @param msgnum Message number of this message within its folder + * @exception MessagingException for failures + */ + protected MimeMessage(Folder folder, InputStream is, int msgnum) + throws MessagingException { + this(folder, msgnum); + initStrict(); + parse(is); + } + + /** + * Constructs a MimeMessage from the given InternetHeaders object + * and content. + * + * This method is for providers subclassing MimeMessage. + * + * @param folder The containing folder. + * @param headers The headers + * @param content The message content + * @param msgnum Message number of this message within its folder + * @exception MessagingException for failures + */ + protected MimeMessage(Folder folder, InternetHeaders headers, + byte[] content, int msgnum) throws MessagingException { + this(folder, msgnum); + this.headers = headers; + this.content = content; + initStrict(); + } + + /** + * Set the strict flag based on property. + */ + private void initStrict() { + if (session != null) { + Properties props = session.getProperties(); + strict = PropUtil.getBooleanProperty(props, + "mail.mime.address.strict", true); + allowutf8 = PropUtil.getBooleanProperty(props, + "mail.mime.allowutf8", false); + } + } + + /** + * Parse the InputStream setting the headers and + * content fields appropriately. Also resets the + * modified flag.

    + * + * This method is intended for use by subclasses that need to + * control when the InputStream is parsed. + * + * @param is The message input stream + * @exception MessagingException for failures + */ + protected void parse(InputStream is) throws MessagingException { + + if (!(is instanceof ByteArrayInputStream) && + !(is instanceof BufferedInputStream) && + !(is instanceof SharedInputStream)) + is = new BufferedInputStream(is); + + headers = createInternetHeaders(is); + + if (is instanceof SharedInputStream) { + SharedInputStream sis = (SharedInputStream)is; + contentStream = sis.newStream(sis.getPosition(), -1); + } else { + try { + content = ASCIIUtility.getBytes(is); + } catch (IOException ioex) { + throw new MessagingException("IOException", ioex); + } + } + + modified = false; + } + + /** + * Returns the value of the RFC 822 "From" header fields. If this + * header field is absent, the "Sender" header field is used. + * If the "Sender" header field is also absent, null + * is returned.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return Address object + * @exception MessagingException for failures + * @see #headers + */ + @Override + public Address[] getFrom() throws MessagingException { + Address[] a = getAddressHeader("From"); + if (a == null) + a = getAddressHeader("Sender"); + + return a; + } + + /** + * Set the RFC 822 "From" header field. Any existing values are + * replaced with the given address. If address is null, + * this header is removed. + * + * @param address the sender of this message + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setFrom(Address address) throws MessagingException { + if (address == null) + removeHeader("From"); + else + setHeader("From", MimeUtility.fold(6, address.toString())); + } + + /** + * Set the RFC 822 "From" header field. Any existing values are + * replaced with the given addresses. If address is null, + * this header is removed. + * + * @param address the sender(s) of this message + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + * @since JvaMail 1.5 + */ + public void setFrom(String address) throws MessagingException { + if (address == null) + removeHeader("From"); + else + setAddressHeader("From", InternetAddress.parse(address)); + } + + /** + * Set the RFC 822 "From" header field using the value of the + * InternetAddress.getLocalAddress method. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setFrom() throws MessagingException { + InternetAddress me = null; + try { + me = InternetAddress._getLocalAddress(session); + } catch (Exception ex) { + // if anything goes wrong (SecurityException, UnknownHostException), + // chain the exception + throw new MessagingException("No From address", ex); + } + if (me != null) + setFrom(me); + else + throw new MessagingException("No From address"); + } + + /** + * Add the specified addresses to the existing "From" field. If + * the "From" field does not already exist, it is created. + * + * @param addresses the senders of this message + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void addFrom(Address[] addresses) throws MessagingException { + addAddressHeader("From", addresses); + } + + /** + * Returns the value of the RFC 822 "Sender" header field. + * If the "Sender" header field is absent, null + * is returned.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return Address object + * @exception MessagingException for failures + * @see #headers + * @since JavaMail 1.3 + */ + public Address getSender() throws MessagingException { + Address[] a = getAddressHeader("Sender"); + if (a == null || a.length == 0) + return null; + return a[0]; // there can be only one + } + + /** + * Set the RFC 822 "Sender" header field. Any existing values are + * replaced with the given address. If address is null, + * this header is removed. + * + * @param address the sender of this message + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + * @since JavaMail 1.3 + */ + public void setSender(Address address) throws MessagingException { + if (address == null) + removeHeader("Sender"); + else + setHeader("Sender", MimeUtility.fold(8, address.toString())); + } + + /** + * This inner class extends the javax.mail.Message.RecipientType + * class to add additional RecipientTypes. The one additional + * RecipientType currently defined here is NEWSGROUPS. + * + * @see javax.mail.Message.RecipientType + */ + public static class RecipientType extends Message.RecipientType { + + private static final long serialVersionUID = -5468290701714395543L; + + /** + * The "Newsgroup" (Usenet news) recipients. + */ + public static final RecipientType NEWSGROUPS = + new RecipientType("Newsgroups"); + protected RecipientType(String type) { + super(type); + } + + @Override + protected Object readResolve() throws ObjectStreamException { + if (type.equals("Newsgroups")) + return NEWSGROUPS; + else + return super.readResolve(); + } + } + + /** + * Returns the recepients specified by the type. The mapping + * between the type and the corresponding RFC 822 header is + * as follows: + *

    +     *		Message.RecipientType.TO		"To"
    +     *		Message.RecipientType.CC		"Cc"
    +     *		Message.RecipientType.BCC		"Bcc"
    +     *		MimeMessage.RecipientType.NEWSGROUPS	"Newsgroups"
    +     * 

    + * + * Returns null if the header specified by the type is not found + * or if its value is empty.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @param type Type of recepient + * @return array of Address objects + * @exception MessagingException if header could not + * be retrieved + * @exception AddressException if the header is misformatted + * @see #headers + * @see javax.mail.Message.RecipientType#TO + * @see javax.mail.Message.RecipientType#CC + * @see javax.mail.Message.RecipientType#BCC + * @see javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS + */ + @Override + public Address[] getRecipients(Message.RecipientType type) + throws MessagingException { + if (type == RecipientType.NEWSGROUPS) { + String s = getHeader("Newsgroups", ","); + return (s == null) ? null : NewsAddress.parse(s); + } else + return getAddressHeader(getHeaderName(type)); + } + + /** + * Get all the recipient addresses for the message. + * Extracts the TO, CC, BCC, and NEWSGROUPS recipients. + * + * @return array of Address objects + * @exception MessagingException for failures + * @see javax.mail.Message.RecipientType#TO + * @see javax.mail.Message.RecipientType#CC + * @see javax.mail.Message.RecipientType#BCC + * @see javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS + */ + @Override + public Address[] getAllRecipients() throws MessagingException { + Address[] all = super.getAllRecipients(); + Address[] ng = getRecipients(RecipientType.NEWSGROUPS); + + if (ng == null) + return all; // the common case + if (all == null) + return ng; // a rare case + + Address[] addresses = new Address[all.length + ng.length]; + System.arraycopy(all, 0, addresses, 0, all.length); + System.arraycopy(ng, 0, addresses, all.length, ng.length); + return addresses; + } + + /** + * Set the specified recipient type to the given addresses. + * If the address parameter is null, the corresponding + * recipient field is removed. + * + * @param type Recipient type + * @param addresses Addresses + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + * @see #getRecipients + */ + @Override + public void setRecipients(Message.RecipientType type, Address[] addresses) + throws MessagingException { + if (type == RecipientType.NEWSGROUPS) { + if (addresses == null || addresses.length == 0) + removeHeader("Newsgroups"); + else + setHeader("Newsgroups", NewsAddress.toString(addresses)); + } else + setAddressHeader(getHeaderName(type), addresses); + } + + /** + * Set the specified recipient type to the given addresses. + * If the address parameter is null, the corresponding + * recipient field is removed. + * + * @param type Recipient type + * @param addresses Addresses + * @exception AddressException if the attempt to parse the + * addresses String fails + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + * @see #getRecipients + * @since JavaMail 1.2 + */ + public void setRecipients(Message.RecipientType type, String addresses) + throws MessagingException { + if (type == RecipientType.NEWSGROUPS) { + if (addresses == null || addresses.length() == 0) + removeHeader("Newsgroups"); + else + setHeader("Newsgroups", addresses); + } else + setAddressHeader(getHeaderName(type), + addresses == null ? null : InternetAddress.parse(addresses)); + } + + /** + * Add the given addresses to the specified recipient type. + * + * @param type Recipient type + * @param addresses Addresses + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void addRecipients(Message.RecipientType type, Address[] addresses) + throws MessagingException { + if (type == RecipientType.NEWSGROUPS) { + String s = NewsAddress.toString(addresses); + if (s != null) + addHeader("Newsgroups", s); + } else + addAddressHeader(getHeaderName(type), addresses); + } + + /** + * Add the given addresses to the specified recipient type. + * + * @param type Recipient type + * @param addresses Addresses + * @exception AddressException if the attempt to parse the + * addresses String fails + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + * @since JavaMail 1.2 + */ + public void addRecipients(Message.RecipientType type, String addresses) + throws MessagingException { + if (type == RecipientType.NEWSGROUPS) { + if (addresses != null && addresses.length() != 0) + addHeader("Newsgroups", addresses); + } else + addAddressHeader(getHeaderName(type), + InternetAddress.parse(addresses)); + } + + /** + * Return the value of the RFC 822 "Reply-To" header field. If + * this header is unavailable or its value is absent, then + * the getFrom method is called and its value is returned. + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @exception MessagingException for failures + * @see #headers + */ + @Override + public Address[] getReplyTo() throws MessagingException { + Address[] a = getAddressHeader("Reply-To"); + if (a == null || a.length == 0) + a = getFrom(); + return a; + } + + /** + * Set the RFC 822 "Reply-To" header field. If the address + * parameter is null, this header is removed. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setReplyTo(Address[] addresses) throws MessagingException { + setAddressHeader("Reply-To", addresses); + } + + // Convenience method to get addresses + private Address[] getAddressHeader(String name) + throws MessagingException { + String s = getHeader(name, ","); + return (s == null) ? null : InternetAddress.parseHeader(s, strict); + } + + // Convenience method to set addresses + private void setAddressHeader(String name, Address[] addresses) + throws MessagingException { + String s; + if (allowutf8) + s = InternetAddress.toUnicodeString(addresses, name.length() + 2); + else + s = InternetAddress.toString(addresses, name.length() + 2); + if (s == null) + removeHeader(name); + else + setHeader(name, s); + } + + private void addAddressHeader(String name, Address[] addresses) + throws MessagingException { + if (addresses == null || addresses.length == 0) + return; + Address[] a = getAddressHeader(name); + Address[] anew; + if (a == null || a.length == 0) + anew = addresses; + else { + anew = new Address[a.length + addresses.length]; + System.arraycopy(a, 0, anew, 0, a.length); + System.arraycopy(addresses, 0, anew, a.length, addresses.length); + } + String s; + if (allowutf8) + s = InternetAddress.toUnicodeString(anew, name.length() + 2); + else + s = InternetAddress.toString(anew, name.length() + 2); + if (s == null) + return; + setHeader(name, s); + } + + /** + * Returns the value of the "Subject" header field. Returns null + * if the subject field is unavailable or its value is absent.

    + * + * If the subject is encoded as per RFC 2047, it is decoded and + * converted into Unicode. If the decoding or conversion fails, the + * raw data is returned as is.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return Subject + * @exception MessagingException for failures + * @see #headers + */ + @Override + public String getSubject() throws MessagingException { + String rawvalue = getHeader("Subject", null); + + if (rawvalue == null) + return null; + + try { + return MimeUtility.decodeText(MimeUtility.unfold(rawvalue)); + } catch (UnsupportedEncodingException ex) { + return rawvalue; + } + } + + /** + * Set the "Subject" header field. If the subject contains + * non US-ASCII characters, it will be encoded using the + * platform's default charset. If the subject contains only + * US-ASCII characters, no encoding is done and it is used + * as-is. If the subject is null, the existing "Subject" field + * is removed.

    + * + * The application must ensure that the subject does not contain + * any line breaks.

    + * + * Note that if the charset encoding process fails, a + * MessagingException is thrown, and an UnsupportedEncodingException + * is included in the chain of nested exceptions within the + * MessagingException. + * + * @param subject The subject + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setSubject(String subject) throws MessagingException { + setSubject(subject, null); + } + + /** + * Set the "Subject" header field. If the subject contains non + * US-ASCII characters, it will be encoded using the specified + * charset. If the subject contains only US-ASCII characters, no + * encoding is done and it is used as-is. If the subject is null, + * the existing "Subject" header field is removed.

    + * + * The application must ensure that the subject does not contain + * any line breaks.

    + * + * Note that if the charset encoding process fails, a + * MessagingException is thrown, and an UnsupportedEncodingException + * is included in the chain of nested exceptions within the + * MessagingException. + * + * @param subject The subject + * @param charset The charset + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + public void setSubject(String subject, String charset) + throws MessagingException { + if (subject == null) { + removeHeader("Subject"); + } else { + try { + setHeader("Subject", MimeUtility.fold(9, + MimeUtility.encodeText(subject, charset, null))); + } catch (UnsupportedEncodingException uex) { + throw new MessagingException("Encoding error", uex); + } + } + } + + /** + * Returns the value of the RFC 822 "Date" field. This is the date + * on which this message was sent. Returns null if this field is + * unavailable or its value is absent.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return The sent Date + * @exception MessagingException for failures + */ + @Override + public Date getSentDate() throws MessagingException { + String s = getHeader("Date", null); + if (s != null) { + try { + synchronized (mailDateFormat) { + return mailDateFormat.parse(s); + } + } catch (ParseException pex) { + return null; + } + } + + return null; + } + + /** + * Set the RFC 822 "Date" header field. This is the date on which the + * creator of the message indicates that the message is complete + * and ready for delivery. If the date parameter is + * null, the existing "Date" field is removed. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setSentDate(Date d) throws MessagingException { + if (d == null) + removeHeader("Date"); + else { + synchronized (mailDateFormat) { + setHeader("Date", mailDateFormat.format(d)); + } + } + } + + /** + * Returns the Date on this message was received. Returns + * null if this date cannot be obtained.

    + * + * Note that RFC 822 does not define a field for the received + * date. Hence only implementations that can provide this date + * need return a valid value.

    + * + * This implementation returns null. + * + * @return the date this message was received + * @exception MessagingException for failures + */ + @Override + public Date getReceivedDate() throws MessagingException { + return null; + } + + /** + * Return the size of the content of this message in bytes. + * Return -1 if the size cannot be determined.

    + * + * Note that this number may not be an exact measure of the + * content size and may or may not account for any transfer + * encoding of the content.

    + * + * This implementation returns the size of the content + * array (if not null), or, if contentStream is not + * null, and the available method returns a positive + * number, it returns that number as the size. Otherwise, it returns + * -1. + * + * @return size of content in bytes + * @exception MessagingException for failures + */ + @Override + public int getSize() throws MessagingException { + if (content != null) + return content.length; + if (contentStream != null) { + try { + int size = contentStream.available(); + // only believe the size if it's greater than zero, since zero + // is the default returned by the InputStream class itself + if (size > 0) + return size; + } catch (IOException ex) { + // ignore it + } + } + return -1; + } + + /** + * Return the number of lines for the content of this message. + * Return -1 if this number cannot be determined.

    + * + * Note that this number may not be an exact measure of the + * content length and may or may not account for any transfer + * encoding of the content.

    + * + * This implementation returns -1. + * + * @return number of lines in the content. + * @exception MessagingException for failures + */ + @Override + public int getLineCount() throws MessagingException { + return -1; + } + + /** + * Returns the value of the RFC 822 "Content-Type" header field. + * This represents the content-type of the content of this + * message. This value must not be null. If this field is + * unavailable, "text/plain" should be returned.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return The ContentType of this part + * @exception MessagingException for failures + * @see javax.activation.DataHandler + */ + @Override + public String getContentType() throws MessagingException { + String s = getHeader("Content-Type", null); + s = MimeUtil.cleanContentType(this, s); + if (s == null) + return "text/plain"; + return s; + } + + /** + * Is this Part of the specified MIME type? This method + * compares only the primaryType and + * subType. + * The parameters of the content types are ignored.

    + * + * For example, this method will return true when + * comparing a Part of content type "text/plain" + * with "text/plain; charset=foobar".

    + * + * If the subType of mimeType is the + * special character '*', then the subtype is ignored during the + * comparison. + * + * @param mimeType the MIME type to check + * @return true if it matches the MIME type + * @exception MessagingException for failures + */ + @Override + public boolean isMimeType(String mimeType) throws MessagingException { + return MimeBodyPart.isMimeType(this, mimeType); + } + + /** + * Returns the disposition from the "Content-Disposition" header field. + * This represents the disposition of this part. The disposition + * describes how the part should be presented to the user.

    + * + * If the Content-Disposition field is unavailable, + * null is returned.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return disposition of this part, or null if unknown + * @exception MessagingException for failures + */ + @Override + public String getDisposition() throws MessagingException { + return MimeBodyPart.getDisposition(this); + } + + /** + * Set the disposition in the "Content-Disposition" header field + * of this body part. If the disposition is null, any existing + * "Content-Disposition" header field is removed. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setDisposition(String disposition) throws MessagingException { + MimeBodyPart.setDisposition(this, disposition); + } + + /** + * Returns the content transfer encoding from the + * "Content-Transfer-Encoding" header + * field. Returns null if the header is unavailable + * or its value is absent.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return content-transfer-encoding + * @exception MessagingException for failures + */ + @Override + public String getEncoding() throws MessagingException { + return MimeBodyPart.getEncoding(this); + } + + /** + * Returns the value of the "Content-ID" header field. Returns + * null if the field is unavailable or its value is + * absent.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return content-ID + * @exception MessagingException for failures + */ + @Override + public String getContentID() throws MessagingException { + return getHeader("Content-Id", null); + } + + /** + * Set the "Content-ID" header field of this Message. + * If the cid parameter is null, any existing + * "Content-ID" is removed. + * + * @param cid the content ID + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + public void setContentID(String cid) throws MessagingException { + if (cid == null) + removeHeader("Content-ID"); + else + setHeader("Content-ID", cid); + } + + /** + * Return the value of the "Content-MD5" header field. Returns + * null if this field is unavailable or its value + * is absent.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return content-MD5 + * @exception MessagingException for failures + */ + @Override + public String getContentMD5() throws MessagingException { + return getHeader("Content-MD5", null); + } + + /** + * Set the "Content-MD5" header field of this Message. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setContentMD5(String md5) throws MessagingException { + setHeader("Content-MD5", md5); + } + + /** + * Returns the "Content-Description" header field of this Message. + * This typically associates some descriptive information with + * this part. Returns null if this field is unavailable or its + * value is absent.

    + * + * If the Content-Description field is encoded as per RFC 2047, + * it is decoded and converted into Unicode. If the decoding or + * conversion fails, the raw data is returned as-is

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return content-description + * @exception MessagingException for failures + */ + @Override + public String getDescription() throws MessagingException { + return MimeBodyPart.getDescription(this); + } + + /** + * Set the "Content-Description" header field for this Message. + * If the description parameter is null, then any + * existing "Content-Description" fields are removed.

    + * + * If the description contains non US-ASCII characters, it will + * be encoded using the platform's default charset. If the + * description contains only US-ASCII characters, no encoding + * is done and it is used as-is.

    + * + * Note that if the charset encoding process fails, a + * MessagingException is thrown, and an UnsupportedEncodingException + * is included in the chain of nested exceptions within the + * MessagingException. + * + * @param description content-description + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException An + * UnsupportedEncodingException may be included + * in the exception chain if the charset + * conversion fails. + */ + @Override + public void setDescription(String description) throws MessagingException { + setDescription(description, null); + } + + /** + * Set the "Content-Description" header field for this Message. + * If the description parameter is null, then any + * existing "Content-Description" fields are removed.

    + * + * If the description contains non US-ASCII characters, it will + * be encoded using the specified charset. If the description + * contains only US-ASCII characters, no encoding is done and + * it is used as-is.

    + * + * Note that if the charset encoding process fails, a + * MessagingException is thrown, and an UnsupportedEncodingException + * is included in the chain of nested exceptions within the + * MessagingException. + * + * @param description Description + * @param charset Charset for encoding + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException An + * UnsupportedEncodingException may be included + * in the exception chain if the charset + * conversion fails. + */ + public void setDescription(String description, String charset) + throws MessagingException { + MimeBodyPart.setDescription(this, description, charset); + } + + /** + * Get the languages specified in the "Content-Language" header + * field of this message. The Content-Language header is defined by + * RFC 1766. Returns null if this field is unavailable + * or its value is absent.

    + * + * This implementation uses the getHeader method + * to obtain the requisite header field. + * + * @return value of content-language header. + * @exception MessagingException for failures + */ + @Override + public String[] getContentLanguage() throws MessagingException { + return MimeBodyPart.getContentLanguage(this); + } + + /** + * Set the "Content-Language" header of this MimePart. The + * Content-Language header is defined by RFC 1766. + * + * @param languages array of language tags + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setContentLanguage(String[] languages) + throws MessagingException { + MimeBodyPart.setContentLanguage(this, languages); + } + + /** + * Returns the value of the "Message-ID" header field. Returns + * null if this field is unavailable or its value is absent.

    + * + * The default implementation provided here uses the + * getHeader method to return the value of the + * "Message-ID" field. + * + * @return Message-ID + * @exception MessagingException if the retrieval of this field + * causes any exception. + * @see javax.mail.search.MessageIDTerm + * @since JavaMail 1.1 + */ + public String getMessageID() throws MessagingException { + return getHeader("Message-ID", null); + } + + /** + * Get the filename associated with this Message.

    + * + * Returns the value of the "filename" parameter from the + * "Content-Disposition" header field of this message. If it's + * not available, returns the value of the "name" parameter from + * the "Content-Type" header field of this BodyPart. + * Returns null if both are absent.

    + * + * If the mail.mime.encodefilename System property + * is set to true, the {@link MimeUtility#decodeText + * MimeUtility.decodeText} method will be used to decode the + * filename. While such encoding is not supported by the MIME + * spec, many mailers use this technique to support non-ASCII + * characters in filenames. The default value of this property + * is false. + * + * @return filename + * @exception MessagingException for failures + */ + @Override + public String getFileName() throws MessagingException { + return MimeBodyPart.getFileName(this); + } + + /** + * Set the filename associated with this part, if possible.

    + * + * Sets the "filename" parameter of the "Content-Disposition" + * header field of this message.

    + * + * If the mail.mime.encodefilename System property + * is set to true, the {@link MimeUtility#encodeText + * MimeUtility.encodeText} method will be used to encode the + * filename. While such encoding is not supported by the MIME + * spec, many mailers use this technique to support non-ASCII + * characters in filenames. The default value of this property + * is false. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setFileName(String filename) throws MessagingException { + MimeBodyPart.setFileName(this, filename); + } + + private String getHeaderName(Message.RecipientType type) + throws MessagingException { + String headerName; + + if (type == Message.RecipientType.TO) + headerName = "To"; + else if (type == Message.RecipientType.CC) + headerName = "Cc"; + else if (type == Message.RecipientType.BCC) + headerName = "Bcc"; + else if (type == MimeMessage.RecipientType.NEWSGROUPS) + headerName = "Newsgroups"; + else + throw new MessagingException("Invalid Recipient Type"); + return headerName; + } + + + /** + * Return a decoded input stream for this Message's "content".

    + * + * This implementation obtains the input stream from the DataHandler, + * that is, it invokes getDataHandler().getInputStream(). + * + * @return an InputStream + * @exception IOException this is typically thrown by the + * DataHandler. Refer to the documentation for + * javax.activation.DataHandler for more details. + * @exception MessagingException for other failures + * + * @see #getContentStream + * @see javax.activation.DataHandler#getInputStream + */ + @Override + public InputStream getInputStream() + throws IOException, MessagingException { + return getDataHandler().getInputStream(); + } + + /** + * Produce the raw bytes of the content. This method is used during + * parsing, to create a DataHandler object for the content. Subclasses + * that can provide a separate input stream for just the message + * content might want to override this method.

    + * + * This implementation returns a SharedInputStream, if + * contentStream is not null. Otherwise, it + * returns a ByteArrayInputStream constructed + * out of the content byte array. + * + * @return an InputStream containing the raw bytes + * @exception MessagingException for failures + * @see #content + */ + protected InputStream getContentStream() throws MessagingException { + if (contentStream != null) + return ((SharedInputStream)contentStream).newStream(0, -1); + if (content != null) + return new SharedByteArrayInputStream(content); + + throw new MessagingException("No MimeMessage content"); + } + + /** + * Return an InputStream to the raw data with any Content-Transfer-Encoding + * intact. This method is useful if the "Content-Transfer-Encoding" + * header is incorrect or corrupt, which would prevent the + * getInputStream method or getContent method + * from returning the correct data. In such a case the application may + * use this method and attempt to decode the raw data itself.

    + * + * This implementation simply calls the getContentStream + * method. + * + * @return an InputStream containing the raw bytes + * @exception MessagingException for failures + * @see #getInputStream + * @see #getContentStream + * @since JavaMail 1.2 + */ + public InputStream getRawInputStream() throws MessagingException { + return getContentStream(); + } + + /** + * Return a DataHandler for this Message's content.

    + * + * The implementation provided here works approximately as follows. + * Note the use of the getContentStream method to + * generate the byte stream for the content. Also note that + * any transfer-decoding is done automatically within this method. + * + *

    +     *  getDataHandler() {
    +     *      if (dh == null) {
    +     *          dh = new DataHandler(new MimePartDataSource(this));
    +     *      }
    +     *      return dh;
    +     *  }
    +     *
    +     *  class MimePartDataSource implements DataSource {
    +     *      public getInputStream() {
    +     *          return MimeUtility.decode(
    +     *		     getContentStream(), getEncoding());
    +     *      }
    +     *	
    +     *		.... <other DataSource methods>
    +     *  }
    +     * 

    + * + * @exception MessagingException for failures + */ + @Override + public synchronized DataHandler getDataHandler() + throws MessagingException { + if (dh == null) + dh = new MimeBodyPart.MimePartDataHandler(this); + return dh; + } + + /** + * Return the content as a Java object. The type of this + * object is dependent on the content itself. For + * example, the native format of a "text/plain" content + * is usually a String object. The native format for a "multipart" + * message is always a Multipart subclass. For content types that are + * unknown to the DataHandler system, an input stream is returned + * as the content.

    + * + * This implementation obtains the content from the DataHandler, + * that is, it invokes getDataHandler().getContent(). + * If the content is a Multipart or Message object and was created by + * parsing a stream, the object is cached and returned in subsequent + * calls so that modifications to the content will not be lost. + * + * @return Object + * @see javax.mail.Part + * @see javax.activation.DataHandler#getContent + * @exception IOException this is typically thrown by the + * DataHandler. Refer to the documentation for + * javax.activation.DataHandler for more details. + * @exception MessagingException for other failures + */ + @Override + public Object getContent() throws IOException, MessagingException { + if (cachedContent != null) + return cachedContent; + Object c; + try { + c = getDataHandler().getContent(); + } catch (FolderClosedIOException fex) { + throw new FolderClosedException(fex.getFolder(), fex.getMessage()); + } catch (MessageRemovedIOException mex) { + throw new MessageRemovedException(mex.getMessage()); + } + if (MimeBodyPart.cacheMultipart && + (c instanceof Multipart || c instanceof Message) && + (content != null || contentStream != null)) { + cachedContent = c; + /* + * We may abandon the input stream so make sure + * the MimeMultipart has consumed the stream. + */ + if (c instanceof MimeMultipart) + ((MimeMultipart)c).parse(); + } + return c; + } + + /** + * This method provides the mechanism to set this part's content. + * The given DataHandler object should wrap the actual content. + * + * @param dh The DataHandler for the content. + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public synchronized void setDataHandler(DataHandler dh) + throws MessagingException { + this.dh = dh; + cachedContent = null; + MimeBodyPart.invalidateContentHeaders(this); + } + + /** + * A convenience method for setting this Message's content.

    + * + * The content is wrapped in a DataHandler object. Note that a + * DataContentHandler class for the specified type should be + * available to the JavaMail implementation for this to work right. + * i.e., to do setContent(foobar, "application/x-foobar"), + * a DataContentHandler for "application/x-foobar" should be installed. + * Refer to the Java Activation Framework for more information. + * + * @param o the content object + * @param type Mime type of the object + * @exception IllegalWriteException if the underlying + * implementation does not support modification of + * existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setContent(Object o, String type) + throws MessagingException { + if (o instanceof Multipart) + setContent((Multipart)o); + else + setDataHandler(new DataHandler(o, type)); + } + + /** + * Convenience method that sets the given String as this + * part's content, with a MIME type of "text/plain". If the + * string contains non US-ASCII characters. it will be encoded + * using the platform's default charset. The charset is also + * used to set the "charset" parameter.

    + * + * Note that there may be a performance penalty if + * text is large, since this method may have + * to scan all the characters to determine what charset to + * use.

    + * + * If the charset is already known, use the + * setText method that takes the charset parameter. + * + * @param text the text content to set + * @exception MessagingException if an error occurs + * @see #setText(String text, String charset) + */ + @Override + public void setText(String text) throws MessagingException { + setText(text, null); + } + + /** + * Convenience method that sets the given String as this part's + * content, with a MIME type of "text/plain" and the specified + * charset. The given Unicode string will be charset-encoded + * using the specified charset. The charset is also used to set + * the "charset" parameter. + * + * @param text the text content to set + * @param charset the charset to use for the text + * @exception MessagingException if an error occurs + */ + @Override + public void setText(String text, String charset) + throws MessagingException { + MimeBodyPart.setText(this, text, charset, "plain"); + } + + /** + * Convenience method that sets the given String as this part's + * content, with a primary MIME type of "text" and the specified + * MIME subtype. The given Unicode string will be charset-encoded + * using the specified charset. The charset is also used to set + * the "charset" parameter. + * + * @param text the text content to set + * @param charset the charset to use for the text + * @param subtype the MIME subtype to use (e.g., "html") + * @exception MessagingException if an error occurs + * @since JavaMail 1.4 + */ + @Override + public void setText(String text, String charset, String subtype) + throws MessagingException { + MimeBodyPart.setText(this, text, charset, subtype); + } + + /** + * This method sets the Message's content to a Multipart object. + * + * @param mp The multipart object that is the Message's content + * @exception IllegalWriteException if the underlying + * implementation does not support modification of + * existing values + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setContent(Multipart mp) throws MessagingException { + setDataHandler(new DataHandler(mp, mp.getContentType())); + mp.setParent(this); + } + + /** + * Get a new Message suitable for a reply to this message. + * The new Message will have its attributes and headers + * set up appropriately. Note that this new message object + * will be empty, i.e., it will not have a "content". + * These will have to be suitably filled in by the client.

    + * + * If replyToAll is set, the new Message will be addressed + * to all recipients of this message. Otherwise, the reply will be + * addressed to only the sender of this message (using the value + * of the getReplyTo method).

    + * + * The "Subject" field is filled in with the original subject + * prefixed with "Re:" (unless it already starts with "Re:"). + * The "In-Reply-To" header is set in the new message if this + * message has a "Message-Id" header. The ANSWERED + * flag is set in this message. + * + * The current implementation also sets the "References" header + * in the new message to include the contents of the "References" + * header (or, if missing, the "In-Reply-To" header) in this message, + * plus the contents of the "Message-Id" header of this message, + * as described in RFC 2822. + * + * @param replyToAll reply should be sent to all recipients + * of this message + * @return the reply Message + * @exception MessagingException for failures + */ + @Override + public Message reply(boolean replyToAll) throws MessagingException { + return reply(replyToAll, true); + } + + /** + * Get a new Message suitable for a reply to this message. + * The new Message will have its attributes and headers + * set up appropriately. Note that this new message object + * will be empty, i.e., it will not have a "content". + * These will have to be suitably filled in by the client.

    + * + * If replyToAll is set, the new Message will be addressed + * to all recipients of this message. Otherwise, the reply will be + * addressed to only the sender of this message (using the value + * of the getReplyTo method).

    + * + * If setAnswered is set, the + * {@link javax.mail.Flags.Flag#ANSWERED ANSWERED} flag is set + * in this message.

    + * + * The "Subject" field is filled in with the original subject + * prefixed with "Re:" (unless it already starts with "Re:"). + * The "In-Reply-To" header is set in the new message if this + * message has a "Message-Id" header. + * + * The current implementation also sets the "References" header + * in the new message to include the contents of the "References" + * header (or, if missing, the "In-Reply-To" header) in this message, + * plus the contents of the "Message-Id" header of this message, + * as described in RFC 2822. + * + * @param replyToAll reply should be sent to all recipients + * of this message + * @param setAnswered set the ANSWERED flag in this message? + * @return the reply Message + * @exception MessagingException for failures + * @since JavaMail 1.5 + */ + public Message reply(boolean replyToAll, boolean setAnswered) + throws MessagingException { + MimeMessage reply = createMimeMessage(session); + /* + * Have to manipulate the raw Subject header so that we don't lose + * any encoding information. This is safe because "Re:" isn't + * internationalized and (generally) isn't encoded. If the entire + * Subject header is encoded, prefixing it with "Re: " still leaves + * a valid and correct encoded header. + */ + String subject = getHeader("Subject", null); + if (subject != null) { + if (!subject.regionMatches(true, 0, "Re: ", 0, 4)) + subject = "Re: " + subject; + reply.setHeader("Subject", subject); + } + Address a[] = getReplyTo(); + reply.setRecipients(Message.RecipientType.TO, a); + if (replyToAll) { + List

    v = new ArrayList<>(); + // add my own address to list + InternetAddress me = InternetAddress.getLocalAddress(session); + if (me != null) + v.add(me); + // add any alternate names I'm known by + String alternates = null; + if (session != null) + alternates = session.getProperty("mail.alternates"); + if (alternates != null) + eliminateDuplicates(v, + InternetAddress.parse(alternates, false)); + // should we Cc all other original recipients? + String replyallccStr = null; + boolean replyallcc = false; + if (session != null) + replyallcc = PropUtil.getBooleanProperty( + session.getProperties(), + "mail.replyallcc", false); + // add the recipients from the To field so far + eliminateDuplicates(v, a); + a = getRecipients(Message.RecipientType.TO); + a = eliminateDuplicates(v, a); + if (a != null && a.length > 0) { + if (replyallcc) + reply.addRecipients(Message.RecipientType.CC, a); + else + reply.addRecipients(Message.RecipientType.TO, a); + } + a = getRecipients(Message.RecipientType.CC); + a = eliminateDuplicates(v, a); + if (a != null && a.length > 0) + reply.addRecipients(Message.RecipientType.CC, a); + // don't eliminate duplicate newsgroups + a = getRecipients(RecipientType.NEWSGROUPS); + if (a != null && a.length > 0) + reply.setRecipients(RecipientType.NEWSGROUPS, a); + } + + String msgId = getHeader("Message-Id", null); + if (msgId != null) + reply.setHeader("In-Reply-To", msgId); + + /* + * Set the References header as described in RFC 2822: + * + * The "References:" field will contain the contents of the parent's + * "References:" field (if any) followed by the contents of the parent's + * "Message-ID:" field (if any). If the parent message does not contain + * a "References:" field but does have an "In-Reply-To:" field + * containing a single message identifier, then the "References:" field + * will contain the contents of the parent's "In-Reply-To:" field + * followed by the contents of the parent's "Message-ID:" field (if + * any). If the parent has none of the "References:", "In-Reply-To:", + * or "Message-ID:" fields, then the new message will have no + * "References:" field. + */ + String refs = getHeader("References", " "); + if (refs == null) { + // XXX - should only use if it contains a single message identifier + refs = getHeader("In-Reply-To", " "); + } + if (msgId != null) { + if (refs != null) + refs = MimeUtility.unfold(refs) + " " + msgId; + else + refs = msgId; + } + if (refs != null) + reply.setHeader("References", MimeUtility.fold(12, refs)); + + if (setAnswered) { + try { + setFlags(answeredFlag, true); + } catch (MessagingException mex) { + // ignore it + } + } + return reply; + } + + // used above in reply() + private static final Flags answeredFlag = new Flags(Flags.Flag.ANSWERED); + + /** + * Check addrs for any duplicates that may already be in v. + * Return a new array without the duplicates. Add any new + * addresses to v. Note that the input array may be modified. + */ + private Address[] eliminateDuplicates(List
    v, Address[] addrs) { + if (addrs == null) + return null; + int gone = 0; + for (int i = 0; i < addrs.length; i++) { + boolean found = false; + // search the list for this address + for (int j = 0; j < v.size(); j++) { + if (((InternetAddress)v.get(j)).equals(addrs[i])) { + // found it; count it and remove it from the input array + found = true; + gone++; + addrs[i] = null; + break; + } + } + if (!found) + v.add(addrs[i]); // add new address to list + } + // if we found any duplicates, squish the array + if (gone != 0) { + Address[] a; + // new array should be same type as original array + // XXX - there must be a better way, perhaps reflection? + if (addrs instanceof InternetAddress[]) + a = new InternetAddress[addrs.length - gone]; + else + a = new Address[addrs.length - gone]; + for (int i = 0, j = 0; i < addrs.length; i++) + if (addrs[i] != null) + a[j++] = addrs[i]; + addrs = a; + } + return addrs; + } + + /** + * Output the message as an RFC 822 format stream.

    + * + * Note that, depending on how the messag was constructed, it may + * use a variety of line termination conventions. Generally the + * output should be sent through an appropriate FilterOutputStream + * that converts the line terminators to the desired form, either + * CRLF for MIME compatibility and for use in Internet protocols, + * or the local platform's line terminator for storage in a local + * text file.

    + * + * This implementation calls the writeTo(OutputStream, + * String[]) method with a null ignore list. + * + * @exception IOException if an error occurs writing to the stream + * or if an error is generated by the + * javax.activation layer. + * @exception MessagingException for other failures + * @see javax.activation.DataHandler#writeTo + */ + @Override + public void writeTo(OutputStream os) + throws IOException, MessagingException { + writeTo(os, null); + } + + /** + * Output the message as an RFC 822 format stream, without + * specified headers. If the saved flag is not set, + * the saveChanges method is called. + * If the modified flag is not + * set and the content array is not null, the + * content array is written directly, after + * writing the appropriate message headers. + * + * @param os the stream to write to + * @param ignoreList the headers to not include in the output + * @exception IOException if an error occurs writing to the stream + * or if an error is generated by the + * javax.activation layer. + * @exception javax.mail.MessagingException for other failures + * @see javax.activation.DataHandler#writeTo + */ + public void writeTo(OutputStream os, String[] ignoreList) + throws IOException, MessagingException { + if (!saved) + saveChanges(); + + if (modified) { + MimeBodyPart.writeTo(this, os, ignoreList); + return; + } + + // Else, the content is untouched, so we can just output it + // First, write out the header + Enumeration hdrLines = getNonMatchingHeaderLines(ignoreList); + LineOutputStream los = new LineOutputStream(os, allowutf8); + while (hdrLines.hasMoreElements()) + los.writeln(hdrLines.nextElement()); + + // The CRLF separator between header and content + los.writeln(); + + // Finally, the content. + if (content == null) { + // call getContentStream to give subclass a chance to + // provide the data on demand + InputStream is = null; + byte[] buf = new byte[8192]; + try { + is = getContentStream(); + // now copy the data to the output stream + int len; + while ((len = is.read(buf)) > 0) + os.write(buf, 0, len); + } finally { + if (is != null) + is.close(); + buf = null; + } + } else { + os.write(content); + } + os.flush(); + } + + /** + * Get all the headers for this header_name. Note that certain + * headers may be encoded as per RFC 2047 if they contain + * non US-ASCII characters and these should be decoded.

    + * + * This implementation obtains the headers from the + * headers InternetHeaders object. + * + * @param name name of header + * @return array of headers + * @exception MessagingException for failures + * @see javax.mail.internet.MimeUtility + */ + @Override + public String[] getHeader(String name) + throws MessagingException { + return headers.getHeader(name); + } + + /** + * Get all the headers for this header name, returned as a single + * String, with headers separated by the delimiter. If the + * delimiter is null, only the first header is + * returned. + * + * @param name the name of this header + * @param delimiter separator between values + * @return the value fields for all headers with + * this name + * @exception MessagingException for failures + */ + @Override + public String getHeader(String name, String delimiter) + throws MessagingException { + return headers.getHeader(name, delimiter); + } + + /** + * Set the value for this header_name. Replaces all existing + * header values with this new value. Note that RFC 822 headers + * must contain only US-ASCII characters, so a header that + * contains non US-ASCII characters must have been encoded by the + * caller as per the rules of RFC 2047. + * + * @param name header name + * @param value header value + * @see javax.mail.internet.MimeUtility + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void setHeader(String name, String value) + throws MessagingException { + headers.setHeader(name, value); + } + + /** + * Add this value to the existing values for this header_name. + * Note that RFC 822 headers must contain only US-ASCII + * characters, so a header that contains non US-ASCII characters + * must have been encoded as per the rules of RFC 2047. + * + * @param name header name + * @param value header value + * @see javax.mail.internet.MimeUtility + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void addHeader(String name, String value) + throws MessagingException { + headers.addHeader(name, value); + } + + /** + * Remove all headers with this name. + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void removeHeader(String name) + throws MessagingException { + headers.removeHeader(name); + } + + /** + * Return all the headers from this Message as an enumeration + * of Header objects.

    + * + * Note that certain headers may be encoded as per RFC 2047 + * if they contain non US-ASCII characters and these should + * be decoded.

    + * + * This implementation obtains the headers from the + * headers InternetHeaders object. + * + * @return array of header objects + * @exception MessagingException for failures + * @see javax.mail.internet.MimeUtility + */ + @Override + public Enumeration

    getAllHeaders() throws MessagingException { + return headers.getAllHeaders(); + } + + /** + * Return matching headers from this Message as an Enumeration of + * Header objects. This implementation obtains the headers from + * the headers InternetHeaders object. + * + * @exception MessagingException for failures + */ + @Override + public Enumeration
    getMatchingHeaders(String[] names) + throws MessagingException { + return headers.getMatchingHeaders(names); + } + + /** + * Return non-matching headers from this Message as an + * Enumeration of Header objects. This implementation + * obtains the header from the headers InternetHeaders object. + * + * @exception MessagingException for failures + */ + @Override + public Enumeration
    getNonMatchingHeaders(String[] names) + throws MessagingException { + return headers.getNonMatchingHeaders(names); + } + + /** + * Add a raw RFC 822 header-line. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void addHeaderLine(String line) throws MessagingException { + headers.addHeaderLine(line); + } + + /** + * Get all header lines as an Enumeration of Strings. A Header + * line is a raw RFC 822 header-line, containing both the "name" + * and "value" field. + * + * @exception MessagingException for failures + */ + @Override + public Enumeration getAllHeaderLines() throws MessagingException { + return headers.getAllHeaderLines(); + } + + /** + * Get matching header lines as an Enumeration of Strings. + * A Header line is a raw RFC 822 header-line, containing both + * the "name" and "value" field. + * + * @exception MessagingException for failures + */ + @Override + public Enumeration getMatchingHeaderLines(String[] names) + throws MessagingException { + return headers.getMatchingHeaderLines(names); + } + + /** + * Get non-matching header lines as an Enumeration of Strings. + * A Header line is a raw RFC 822 header-line, containing both + * the "name" and "value" field. + * + * @exception MessagingException for failures + */ + @Override + public Enumeration getNonMatchingHeaderLines(String[] names) + throws MessagingException { + return headers.getNonMatchingHeaderLines(names); + } + + /** + * Return a Flags object containing the flags for + * this message.

    + * + * Note that a clone of the internal Flags object is returned, so + * modifying the returned Flags object will not affect the flags + * of this message. + * + * @return Flags object containing the flags for this message + * @exception MessagingException for failures + * @see javax.mail.Flags + */ + @Override + public synchronized Flags getFlags() throws MessagingException { + return (Flags)flags.clone(); + } + + /** + * Check whether the flag specified in the flag + * argument is set in this message.

    + * + * This implementation checks this message's internal + * flags object. + * + * @param flag the flag + * @return value of the specified flag for this message + * @exception MessagingException for failures + * @see javax.mail.Flags.Flag + * @see javax.mail.Flags.Flag#ANSWERED + * @see javax.mail.Flags.Flag#DELETED + * @see javax.mail.Flags.Flag#DRAFT + * @see javax.mail.Flags.Flag#FLAGGED + * @see javax.mail.Flags.Flag#RECENT + * @see javax.mail.Flags.Flag#SEEN + */ + @Override + public synchronized boolean isSet(Flags.Flag flag) + throws MessagingException { + return (flags.contains(flag)); + } + + /** + * Set the flags for this message.

    + * + * This implementation modifies the flags field. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public synchronized void setFlags(Flags flag, boolean set) + throws MessagingException { + if (set) + flags.add(flag); + else + flags.remove(flag); + } + + /** + * Updates the appropriate header fields of this message to be + * consistent with the message's contents. If this message is + * contained in a Folder, any changes made to this message are + * committed to the containing folder.

    + * + * If any part of a message's headers or contents are changed, + * saveChanges must be called to ensure that those + * changes are permanent. Otherwise, any such modifications may or + * may not be saved, depending on the folder implementation.

    + * + * Messages obtained from folders opened READ_ONLY should not be + * modified and saveChanges should not be called on such messages.

    + * + * This method sets the modified flag to true, the + * save flag to true, and then calls the + * updateHeaders method. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + @Override + public void saveChanges() throws MessagingException { + modified = true; + saved = true; + updateHeaders(); + } + + /** + * Update the Message-ID header. This method is called + * by the updateHeaders and allows a subclass + * to override only the algorithm for choosing a Message-ID. + * + * @exception MessagingException for failures + * @since JavaMail 1.4 + */ + protected void updateMessageID() throws MessagingException { + setHeader("Message-ID", + "<" + UniqueValue.getUniqueMessageIDValue(session) + ">"); + + } + + /** + * Called by the saveChanges method to actually + * update the MIME headers. The implementation here sets the + * Content-Transfer-Encoding header (if needed + * and not already set), the Date header (if + * not already set), the MIME-Version header + * and the Message-ID header. Also, if the content + * of this message is a MimeMultipart, its + * updateHeaders method is called.

    + * + * If the {@link #cachedContent} field is not null (that is, + * it references a Multipart or Message object), then + * that object is used to set a new DataHandler, any + * stream data used to create this object is discarded, + * and the {@link #cachedContent} field is cleared. + * + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this message is + * obtained from a READ_ONLY folder. + * @exception MessagingException for other failures + */ + protected synchronized void updateHeaders() throws MessagingException { + MimeBodyPart.updateHeaders(this); + setHeader("MIME-Version", "1.0"); + if (getHeader("Date") == null) + setSentDate(new Date()); + updateMessageID(); + + if (cachedContent != null) { + dh = new DataHandler(cachedContent, getContentType()); + cachedContent = null; + content = null; + if (contentStream != null) { + try { + contentStream.close(); + } catch (IOException ioex) { } // nothing to do + } + contentStream = null; + } + } + + /** + * Create and return an InternetHeaders object that loads the + * headers from the given InputStream. Subclasses can override + * this method to return a subclass of InternetHeaders, if + * necessary. This implementation simply constructs and returns + * an InternetHeaders object. + * + * @return an InternetHeaders object + * @param is the InputStream to read the headers from + * @exception MessagingException for failures + * @since JavaMail 1.2 + */ + protected InternetHeaders createInternetHeaders(InputStream is) + throws MessagingException { + return new InternetHeaders(is, allowutf8); + } + + /** + * Create and return a MimeMessage object. The reply method + * uses this method to create the MimeMessage object that it + * will return. Subclasses can override this method to return + * a subclass of MimeMessage. This implementation simply constructs + * and returns a MimeMessage object using the supplied Session. + * + * @param session the Session to use for the new message + * @return the new MimeMessage object + * @exception MessagingException for failures + * @since JavaMail 1.4 + */ + protected MimeMessage createMimeMessage(Session session) + throws MessagingException { + return new MimeMessage(session); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/MimeMultipart.java b/fine-third-default/fine-mail/src/javax/mail/internet/MimeMultipart.java new file mode 100644 index 000000000..0fc863019 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/MimeMultipart.java @@ -0,0 +1,1033 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import javax.mail.*; +import javax.activation.*; +import java.util.*; +import java.io.*; +import com.sun.mail.util.LineOutputStream; +import com.sun.mail.util.LineInputStream; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.util.PropUtil; + +/** + * The MimeMultipart class is an implementation of the abstract Multipart + * class that uses MIME conventions for the multipart data.

    + * + * A MimeMultipart is obtained from a MimePart whose primary type + * is "multipart" (by invoking the part's getContent() method) + * or it can be created by a client as part of creating a new MimeMessage.

    + * + * The default multipart subtype is "mixed". The other multipart + * subtypes, such as "alternative", "related", and so on, can be + * implemented as subclasses of MimeMultipart with additional methods + * to implement the additional semantics of that type of multipart + * content. The intent is that service providers, mail JavaBean writers + * and mail clients will write many such subclasses and their Command + * Beans, and will install them into the JavaBeans Activation + * Framework, so that any JavaMail implementation and its clients can + * transparently find and use these classes. Thus, a MIME multipart + * handler is treated just like any other type handler, thereby + * decoupling the process of providing multipart handlers from the + * JavaMail API. Lacking these additional MimeMultipart subclasses, + * all subtypes of MIME multipart data appear as MimeMultipart objects.

    + * + * An application can directly construct a MIME multipart object of any + * subtype by using the MimeMultipart(String subtype) + * constructor. For example, to create a "multipart/alternative" object, + * use new MimeMultipart("alternative").

    + * + * The mail.mime.multipart.ignoremissingendboundary + * property may be set to false to cause a + * MessagingException to be thrown if the multipart + * data does not end with the required end boundary line. If this + * property is set to true or not set, missing end + * boundaries are not considered an error and the final body part + * ends at the end of the data.

    + * + * The mail.mime.multipart.ignoremissingboundaryparameter + * System property may be set to false to cause a + * MessagingException to be thrown if the Content-Type + * of the MimeMultipart does not include a boundary parameter. + * If this property is set to true or not set, the multipart + * parsing code will look for a line that looks like a bounary line and + * use that as the boundary separating the parts.

    + * + * The mail.mime.multipart.ignoreexistingboundaryparameter + * System property may be set to true to cause any boundary + * to be ignored and instead search for a boundary line in the message + * as with mail.mime.multipart.ignoremissingboundaryparameter.

    + * + * Normally, when writing out a MimeMultipart that contains no body + * parts, or when trying to parse a multipart message with no body parts, + * a MessagingException is thrown. The MIME spec does not allow + * multipart content with no body parts. The + * mail.mime.multipart.allowempty System property may be set to + * true to override this behavior. + * When writing out such a MimeMultipart, a single empty part will be + * included. When reading such a multipart, a MimeMultipart will be created + * with no body parts. + * + * @author John Mani + * @author Bill Shannon + * @author Max Spivak + */ + +public class MimeMultipart extends Multipart { + + /** + * The DataSource supplying our InputStream. + */ + protected DataSource ds = null; + + /** + * Have we parsed the data from our InputStream yet? + * Defaults to true; set to false when our constructor is + * given a DataSource with an InputStream that we need to + * parse. + */ + protected boolean parsed = true; + + /** + * Have we seen the final bounary line? + * + * @since JavaMail 1.5 + */ + protected boolean complete = true; + + /** + * The MIME multipart preamble text, the text that + * occurs before the first boundary line. + * + * @since JavaMail 1.5 + */ + protected String preamble = null; + + /** + * Flag corresponding to the "mail.mime.multipart.ignoremissingendboundary" + * property, set in the {@link #initializeProperties} method called from + * constructors and the parse method. + * + * @since JavaMail 1.5 + */ + protected boolean ignoreMissingEndBoundary = true; + + /** + * Flag corresponding to the + * "mail.mime.multipart.ignoremissingboundaryparameter" + * property, set in the {@link #initializeProperties} method called from + * constructors and the parse method. + * + * @since JavaMail 1.5 + */ + protected boolean ignoreMissingBoundaryParameter = true; + + /** + * Flag corresponding to the + * "mail.mime.multipart.ignoreexistingboundaryparameter" + * property, set in the {@link #initializeProperties} method called from + * constructors and the parse method. + * + * @since JavaMail 1.5 + */ + protected boolean ignoreExistingBoundaryParameter = false; + + /** + * Flag corresponding to the "mail.mime.multipart.allowempty" + * property, set in the {@link #initializeProperties} method called from + * constructors and the parse method. + * + * @since JavaMail 1.5 + */ + protected boolean allowEmpty = false; + + /** + * Default constructor. An empty MimeMultipart object + * is created. Its content type is set to "multipart/mixed". + * A unique boundary string is generated and this string is + * setup as the "boundary" parameter for the + * contentType field.

    + * + * MimeBodyParts may be added later. + */ + public MimeMultipart() { + this("mixed"); + } + + /** + * Construct a MimeMultipart object of the given subtype. + * A unique boundary string is generated and this string is + * setup as the "boundary" parameter for the + * contentType field. + * Calls the {@link #initializeProperties} method.

    + * + * MimeBodyParts may be added later. + * + * @param subtype the MIME content subtype + */ + public MimeMultipart(String subtype) { + super(); + /* + * Compute a boundary string. + */ + String boundary = UniqueValue.getUniqueBoundaryValue(); + ContentType cType = new ContentType("multipart", subtype, null); + cType.setParameter("boundary", boundary); + contentType = cType.toString(); + initializeProperties(); + } + + /** + * Construct a MimeMultipart object of the default "mixed" subtype, + * and with the given body parts. More body parts may be added later. + * + * @param parts the body parts + * @exception MessagingException for failures + * @since JavaMail 1.5 + */ + public MimeMultipart(BodyPart... parts) throws MessagingException { + this(); + for (BodyPart bp : parts) + super.addBodyPart(bp); + } + + /** + * Construct a MimeMultipart object of the given subtype + * and with the given body parts. More body parts may be added later. + * + * @param subtype the MIME content subtype + * @param parts the body parts + * @exception MessagingException for failures + * @since JavaMail 1.5 + */ + public MimeMultipart(String subtype, BodyPart... parts) + throws MessagingException { + this(subtype); + for (BodyPart bp : parts) + super.addBodyPart(bp); + } + + /** + * Constructs a MimeMultipart object and its bodyparts from the + * given DataSource.

    + * + * This constructor handles as a special case the situation where the + * given DataSource is a MultipartDataSource object. In this case, this + * method just invokes the superclass (i.e., Multipart) constructor + * that takes a MultipartDataSource object.

    + * + * Otherwise, the DataSource is assumed to provide a MIME multipart + * byte stream. The parsed flag is set to false. When + * the data for the body parts are needed, the parser extracts the + * "boundary" parameter from the content type of this DataSource, + * skips the 'preamble' and reads bytes till the terminating + * boundary and creates MimeBodyParts for each part of the stream. + * + * @param ds DataSource, can be a MultipartDataSource + * @exception ParseException for failures parsing the message + * @exception MessagingException for other failures + */ + public MimeMultipart(DataSource ds) throws MessagingException { + super(); + + if (ds instanceof MessageAware) { + MessageContext mc = ((MessageAware)ds).getMessageContext(); + setParent(mc.getPart()); + } + + if (ds instanceof MultipartDataSource) { + // ask super to do this for us. + setMultipartDataSource((MultipartDataSource)ds); + return; + } + + // 'ds' was not a MultipartDataSource, we have + // to parse this ourself. + parsed = false; + this.ds = ds; + contentType = ds.getContentType(); + } + + /** + * Initialize flags that control parsing behavior, + * based on System properties described above in + * the class documentation. + * + * @since JavaMail 1.5 + */ + protected void initializeProperties() { + // read properties that control parsing + + // default to true + ignoreMissingEndBoundary = PropUtil.getBooleanSystemProperty( + "mail.mime.multipart.ignoremissingendboundary", true); + // default to true + ignoreMissingBoundaryParameter = PropUtil.getBooleanSystemProperty( + "mail.mime.multipart.ignoremissingboundaryparameter", true); + // default to false + ignoreExistingBoundaryParameter = PropUtil.getBooleanSystemProperty( + "mail.mime.multipart.ignoreexistingboundaryparameter", false); + // default to false + allowEmpty = PropUtil.getBooleanSystemProperty( + "mail.mime.multipart.allowempty", false); + } + + /** + * Set the subtype. This method should be invoked only on a new + * MimeMultipart object created by the client. The default subtype + * of such a multipart object is "mixed".

    + * + * @param subtype Subtype + * @exception MessagingException for failures + */ + public synchronized void setSubType(String subtype) + throws MessagingException { + ContentType cType = new ContentType(contentType); + cType.setSubType(subtype); + contentType = cType.toString(); + } + + /** + * Return the number of enclosed BodyPart objects. + * + * @return number of parts + */ + @Override + public synchronized int getCount() throws MessagingException { + parse(); + return super.getCount(); + } + + /** + * Get the specified BodyPart. BodyParts are numbered starting at 0. + * + * @param index the index of the desired BodyPart + * @return the Part + * @exception MessagingException if no such BodyPart exists + */ + @Override + public synchronized BodyPart getBodyPart(int index) + throws MessagingException { + parse(); + return super.getBodyPart(index); + } + + /** + * Get the MimeBodyPart referred to by the given ContentID (CID). + * Returns null if the part is not found. + * + * @param CID the ContentID of the desired part + * @return the Part + * @exception MessagingException for failures + */ + public synchronized BodyPart getBodyPart(String CID) + throws MessagingException { + parse(); + + int count = getCount(); + for (int i = 0; i < count; i++) { + MimeBodyPart part = (MimeBodyPart)getBodyPart(i); + String s = part.getContentID(); + if (s != null && s.equals(CID)) + return part; + } + return null; + } + + /** + * Remove the specified part from the multipart message. + * Shifts all the parts after the removed part down one. + * + * @param part The part to remove + * @return true if part removed, false otherwise + * @exception MessagingException if no such Part exists + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + */ + @Override + public boolean removeBodyPart(BodyPart part) throws MessagingException { + parse(); + return super.removeBodyPart(part); + } + + /** + * Remove the part at specified location (starting from 0). + * Shifts all the parts after the removed part down one. + * + * @param index Index of the part to remove + * @exception IndexOutOfBoundsException if the given index + * is out of range. + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception MessagingException for other failures + */ + @Override + public void removeBodyPart(int index) throws MessagingException { + parse(); + super.removeBodyPart(index); + } + + /** + * Adds a Part to the multipart. The BodyPart is appended to + * the list of existing Parts. + * + * @param part The Part to be appended + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception MessagingException for other failures + */ + @Override + public synchronized void addBodyPart(BodyPart part) + throws MessagingException { + parse(); + super.addBodyPart(part); + } + + /** + * Adds a BodyPart at position index. + * If index is not the last one in the list, + * the subsequent parts are shifted up. If index + * is larger than the number of parts present, the + * BodyPart is appended to the end. + * + * @param part The BodyPart to be inserted + * @param index Location where to insert the part + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * of existing values + * @exception MessagingException for other failures + */ + @Override + public synchronized void addBodyPart(BodyPart part, int index) + throws MessagingException { + parse(); + super.addBodyPart(part, index); + } + + /** + * Return true if the final boundary line for this + * multipart was seen. When parsing multipart content, + * this class will (by default) terminate parsing with + * no error if the end of input is reached before seeing + * the final multipart boundary line. In such a case, + * this method will return false. (If the System property + * "mail.mime.multipart.ignoremissingendboundary" is set to + * false, parsing such a message will instead throw a + * MessagingException.) + * + * @return true if the final boundary line was seen + * @exception MessagingException for failures + * @since JavaMail 1.4 + */ + public synchronized boolean isComplete() throws MessagingException { + parse(); + return complete; + } + + /** + * Get the preamble text, if any, that appears before the + * first body part of this multipart. Some protocols, + * such as IMAP, will not allow access to the preamble text. + * + * @return the preamble text, or null if no preamble + * @exception MessagingException for failures + * @since JavaMail 1.4 + */ + public synchronized String getPreamble() throws MessagingException { + parse(); + return preamble; + } + + /** + * Set the preamble text to be included before the first + * body part. Applications should generally not include + * any preamble text. In some cases it may be helpful to + * include preamble text with instructions for users of + * pre-MIME software. The preamble text should be complete + * lines, including newlines. + * + * @param preamble the preamble text + * @exception MessagingException for failures + * @since JavaMail 1.4 + */ + public synchronized void setPreamble(String preamble) + throws MessagingException { + this.preamble = preamble; + } + + /** + * Update headers. The default implementation here just + * calls the updateHeaders method on each of its + * children BodyParts.

    + * + * Note that the boundary parameter is already set up when + * a new and empty MimeMultipart object is created.

    + * + * This method is called when the saveChanges + * method is invoked on the Message object containing this + * Multipart. This is typically done as part of the Message + * send process, however note that a client is free to call + * it any number of times. So if the header updating process is + * expensive for a specific MimeMultipart subclass, then it + * might itself want to track whether its internal state actually + * did change, and do the header updating only if necessary. + * + * @exception MessagingException for failures + */ + protected synchronized void updateHeaders() throws MessagingException { + parse(); + for (int i = 0; i < parts.size(); i++) + ((MimeBodyPart)parts.elementAt(i)).updateHeaders(); + } + + /** + * Iterates through all the parts and outputs each MIME part + * separated by a boundary. + */ + @Override + public synchronized void writeTo(OutputStream os) + throws IOException, MessagingException { + parse(); + + String boundary = "--" + + (new ContentType(contentType)).getParameter("boundary"); + LineOutputStream los = new LineOutputStream(os); + + // if there's a preamble, write it out + if (preamble != null) { + byte[] pb = ASCIIUtility.getBytes(preamble); + los.write(pb); + // make sure it ends with a newline + if (pb.length > 0 && + !(pb[pb.length-1] == '\r' || pb[pb.length-1] == '\n')) { + los.writeln(); + } + // XXX - could force a blank line before start boundary + } + + if (parts.size() == 0) { + if (allowEmpty) { + // write out a single empty body part + los.writeln(boundary); // put out boundary + los.writeln(); // put out empty line + } else { + throw new MessagingException("Empty multipart: " + contentType); + } + } else { + for (int i = 0; i < parts.size(); i++) { + los.writeln(boundary); // put out boundary + ((MimeBodyPart)parts.elementAt(i)).writeTo(os); + los.writeln(); // put out empty line + } + } + + // put out last boundary + los.writeln(boundary + "--"); + } + + /** + * Parse the InputStream from our DataSource, constructing the + * appropriate MimeBodyParts. The parsed flag is + * set to true, and if true on entry nothing is done. This + * method is called by all other methods that need data for + * the body parts, to make sure the data has been parsed. + * The {@link #initializeProperties} method is called before + * parsing the data. + * + * @exception ParseException for failures parsing the message + * @exception MessagingException for other failures + * @since JavaMail 1.2 + */ + protected synchronized void parse() throws MessagingException { + if (parsed) + return; + + initializeProperties(); + + InputStream in = null; + SharedInputStream sin = null; + long start = 0, end = 0; + + try { + in = ds.getInputStream(); + if (!(in instanceof ByteArrayInputStream) && + !(in instanceof BufferedInputStream) && + !(in instanceof SharedInputStream)) + in = new BufferedInputStream(in); + } catch (Exception ex) { + throw new MessagingException("No inputstream from datasource", ex); + } + if (in instanceof SharedInputStream) + sin = (SharedInputStream)in; + + ContentType cType = new ContentType(contentType); + String boundary = null; + if (!ignoreExistingBoundaryParameter) { + String bp = cType.getParameter("boundary"); + if (bp != null) + boundary = "--" + bp; + } + if (boundary == null && !ignoreMissingBoundaryParameter && + !ignoreExistingBoundaryParameter) + throw new ParseException("Missing boundary parameter"); + + try { + // Skip and save the preamble + LineInputStream lin = new LineInputStream(in); + StringBuilder preamblesb = null; + String line; + while ((line = lin.readLine()) != null) { + /* + * Strip trailing whitespace. Can't use trim method + * because it's too aggressive. Some bogus MIME + * messages will include control characters in the + * boundary string. + */ + int i; + for (i = line.length() - 1; i >= 0; i--) { + char c = line.charAt(i); + if (!(c == ' ' || c == '\t')) + break; + } + line = line.substring(0, i + 1); + if (boundary != null) { + if (line.equals(boundary)) + break; + if (line.length() == boundary.length() + 2 && + line.startsWith(boundary) && line.endsWith("--")) { + line = null; // signal end of multipart + break; + } + } else { + /* + * Boundary hasn't been defined, does this line + * look like a boundary? If so, assume it is + * the boundary and save it. + */ + if (line.length() > 2 && line.startsWith("--")) { + if (line.length() > 4 && allDashes(line)) { + /* + * The first boundary-like line we find is + * probably *not* the end-of-multipart boundary + * line. More likely it's a line full of dashes + * in the preamble text. Just keep reading. + */ + } else { + boundary = line; + break; + } + } + } + + // save the preamble after skipping blank lines + if (line.length() > 0) { + // accumulate the preamble + if (preamblesb == null) + preamblesb = new StringBuilder(line.length() + 2); + preamblesb.append(line).append(System.lineSeparator()); + } + } + + if (preamblesb != null) + preamble = preamblesb.toString(); + + if (line == null) { + if (allowEmpty) + return; + else + throw new ParseException("Missing start boundary"); + } + + // save individual boundary bytes for comparison later + byte[] bndbytes = ASCIIUtility.getBytes(boundary); + int bl = bndbytes.length; + + /* + * Compile Boyer-Moore parsing tables. + */ + + // initialize Bad Character Shift table + int[] bcs = new int[256]; + for (int i = 0; i < bl; i++) + bcs[bndbytes[i] & 0xff] = i + 1; + + // initialize Good Suffix Shift table + int[] gss = new int[bl]; + NEXT: + for (int i = bl; i > 0; i--) { + int j; // the beginning index of the suffix being considered + for (j = bl - 1; j >= i; j--) { + // Testing for good suffix + if (bndbytes[j] == bndbytes[j - i]) { + // bndbytes[j..len] is a good suffix + gss[j - 1] = i; + } else { + // No match. The array has already been + // filled up with correct values before. + continue NEXT; + } + } + while (j > 0) + gss[--j] = i; + } + gss[bl - 1] = 1; + + /* + * Read and process body parts until we see the + * terminating boundary line (or EOF). + */ + boolean done = false; + getparts: + while (!done) { + InternetHeaders headers = null; + if (sin != null) { + start = sin.getPosition(); + // skip headers + while ((line = lin.readLine()) != null && line.length() > 0) + ; + if (line == null) { + if (!ignoreMissingEndBoundary) + throw new ParseException( + "missing multipart end boundary"); + // assume there's just a missing end boundary + complete = false; + break getparts; + } + } else { + // collect the headers for this body part + headers = createInternetHeaders(in); + } + + if (!in.markSupported()) + throw new MessagingException("Stream doesn't support mark"); + + ByteArrayOutputStream buf = null; + // if we don't have a shared input stream, we copy the data + if (sin == null) + buf = new ByteArrayOutputStream(); + else + end = sin.getPosition(); + int b; + + /* + * These buffers contain the bytes we're checking + * for a match. inbuf is the current buffer and + * previnbuf is the previous buffer. We need the + * previous buffer to check that we're preceeded + * by an EOL. + */ + // XXX - a smarter algorithm would use a sliding window + // over a larger buffer + byte[] inbuf = new byte[bl]; + byte[] previnbuf = new byte[bl]; + int inSize = 0; // number of valid bytes in inbuf + int prevSize = 0; // number of valid bytes in previnbuf + int eolLen; + boolean first = true; + + /* + * Read and save the content bytes in buf. + */ + for (;;) { + in.mark(bl + 4 + 1000); // bnd + "--\r\n" + lots of LWSP + eolLen = 0; + inSize = readFully(in, inbuf, 0, bl); + if (inSize < bl) { + // hit EOF + if (!ignoreMissingEndBoundary) + throw new ParseException( + "missing multipart end boundary"); + if (sin != null) + end = sin.getPosition(); + complete = false; + done = true; + break; + } + // check whether inbuf contains a boundary string + int i; + for (i = bl - 1; i >= 0; i--) { + if (inbuf[i] != bndbytes[i]) + break; + } + if (i < 0) { // matched all bytes + eolLen = 0; + if (!first) { + // working backwards, find out if we were preceeded + // by an EOL, and if so find its length + b = previnbuf[prevSize - 1]; + if (b == '\r' || b == '\n') { + eolLen = 1; + if (b == '\n' && prevSize >= 2) { + b = previnbuf[prevSize - 2]; + if (b == '\r') + eolLen = 2; + } + } + } + if (first || eolLen > 0) { // yes, preceed by EOL + if (sin != null) { + // update "end", in case this really is + // a valid boundary + end = sin.getPosition() - bl - eolLen; + } + // matched the boundary, check for last boundary + int b2 = in.read(); + if (b2 == '-') { + if (in.read() == '-') { + complete = true; + done = true; + break; // ignore trailing text + } + } + // skip linear whitespace + while (b2 == ' ' || b2 == '\t') + b2 = in.read(); + // check for end of line + if (b2 == '\n') + break; // got it! break out of the loop + if (b2 == '\r') { + in.mark(1); + if (in.read() != '\n') + in.reset(); + break; // got it! break out of the loop + } + } + i = 0; + } + + /* + * Get here if boundary didn't match, + * wasn't preceeded by EOL, or wasn't + * followed by whitespace or EOL. + */ + + // compute how many bytes we can skip + int skip = Math.max(i + 1 - bcs[inbuf[i] & 0x7f], gss[i]); + // want to keep at least two characters + if (skip < 2) { + // only skipping one byte, save one byte + // from previous buffer as well + // first, write out bytes we're done with + if (sin == null && prevSize > 1) + buf.write(previnbuf, 0, prevSize - 1); + in.reset(); + skipFully(in, 1); + if (prevSize >= 1) { // is there a byte to save? + // yes, save one from previous and one from current + previnbuf[0] = previnbuf[prevSize - 1]; + previnbuf[1] = inbuf[0]; + prevSize = 2; + } else { + // no previous bytes to save, can only save current + previnbuf[0] = inbuf[0]; + prevSize = 1; + } + } else { + // first, write out data from previous buffer before + // we dump it + if (prevSize > 0 && sin == null) + buf.write(previnbuf, 0, prevSize); + // all the bytes we're skipping are saved in previnbuf + prevSize = skip; + in.reset(); + skipFully(in, prevSize); + // swap buffers + byte[] tmp = inbuf; + inbuf = previnbuf; + previnbuf = tmp; + } + first = false; + } + + /* + * Create a MimeBody element to represent this body part. + */ + MimeBodyPart part; + if (sin != null) { + part = createMimeBodyPartIs(sin.newStream(start, end)); + } else { + // write out data from previous buffer, not including EOL + if (prevSize - eolLen > 0) + buf.write(previnbuf, 0, prevSize - eolLen); + // if we didn't find a trailing boundary, + // the current buffer has data we need too + if (!complete && inSize > 0) + buf.write(inbuf, 0, inSize); + part = createMimeBodyPart(headers, buf.toByteArray()); + } + super.addBodyPart(part); + } + } catch (IOException ioex) { + throw new MessagingException("IO Error", ioex); + } finally { + try { + in.close(); + } catch (IOException cex) { + // ignore + } + } + + parsed = true; + } + + /** + * Is the string all dashes ('-')? + */ + private static boolean allDashes(String s) { + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) != '-') + return false; + } + return true; + } + + /** + * Read data from the input stream to fill the buffer starting + * at the specified offset with the specified number of bytes. + * If len is zero, return zero. If at EOF, return -1. Otherwise, + * return the number of bytes read. Call the read method on the + * input stream as many times as necessary to read len bytes. + * + * @param in InputStream to read from + * @param buf buffer to read into + * @param off offset in the buffer for first byte + * @param len number of bytes to read + * @return -1 on EOF, otherwise number of bytes read + * @exception IOException on I/O errors + */ + private static int readFully(InputStream in, byte[] buf, int off, int len) + throws IOException { + if (len == 0) + return 0; + int total = 0; + while (len > 0) { + int bsize = in.read(buf, off, len); + if (bsize <= 0) // should never be zero + break; + off += bsize; + total += bsize; + len -= bsize; + } + return total > 0 ? total : -1; + } + + /** + * Skip the specified number of bytes, repeatedly calling + * the skip method as necessary. + */ + private void skipFully(InputStream in, long offset) throws IOException { + while (offset > 0) { + long cur = in.skip(offset); + if (cur <= 0) + throw new EOFException("can't skip"); + offset -= cur; + } + } + + /** + * Create and return an InternetHeaders object that loads the + * headers from the given InputStream. Subclasses can override + * this method to return a subclass of InternetHeaders, if + * necessary. This implementation simply constructs and returns + * an InternetHeaders object. + * + * @param is the InputStream to read the headers from + * @return an InternetHeaders object + * @exception MessagingException for failures + * @since JavaMail 1.2 + */ + protected InternetHeaders createInternetHeaders(InputStream is) + throws MessagingException { + return new InternetHeaders(is); + } + + /** + * Create and return a MimeBodyPart object to represent a + * body part parsed from the InputStream. Subclasses can override + * this method to return a subclass of MimeBodyPart, if + * necessary. This implementation simply constructs and returns + * a MimeBodyPart object. + * + * @param headers the headers for the body part + * @param content the content of the body part + * @return a MimeBodyPart + * @exception MessagingException for failures + * @since JavaMail 1.2 + */ + protected MimeBodyPart createMimeBodyPart(InternetHeaders headers, + byte[] content) throws MessagingException { + return new MimeBodyPart(headers, content); + } + + /** + * Create and return a MimeBodyPart object to represent a + * body part parsed from the InputStream. Subclasses can override + * this method to return a subclass of MimeBodyPart, if + * necessary. This implementation simply constructs and returns + * a MimeBodyPart object. + * + * @param is InputStream containing the body part + * @return a MimeBodyPart + * @exception MessagingException for failures + * @since JavaMail 1.2 + */ + protected MimeBodyPart createMimeBodyPart(InputStream is) + throws MessagingException { + return new MimeBodyPart(is); + } + + private MimeBodyPart createMimeBodyPartIs(InputStream is) + throws MessagingException { + try { + return createMimeBodyPart(is); + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore it + } + } + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/MimePart.java b/fine-third-default/fine-mail/src/javax/mail/internet/MimePart.java new file mode 100644 index 000000000..6f1099fc3 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/MimePart.java @@ -0,0 +1,248 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import javax.mail.*; +import java.io.*; +import java.util.Enumeration; + +/** + * The MimePart interface models an Entity as defined + * by MIME (RFC2045, Section 2.4).

    + * + * MimePart extends the Part interface to add additional RFC822 and MIME + * specific semantics and attributes. It provides the base interface for + * the MimeMessage and MimeBodyPart classes + * + *


    A note on RFC822 and MIME headers

    + * + * RFC822 and MIME header fields must contain only + * US-ASCII characters. If a header contains non US-ASCII characters, + * it must be encoded as per the rules in RFC 2047. The MimeUtility + * class provided in this package can be used to to achieve this. + * Callers of the setHeader, addHeader, and + * addHeaderLine methods are responsible for enforcing + * the MIME requirements for the specified headers. In addition, these + * header fields must be folded (wrapped) before being sent if they + * exceed the line length limitation for the transport (1000 bytes for + * SMTP). Received headers may have been folded. The application is + * responsible for folding and unfolding headers as appropriate.

    + * + * @see MimeUtility + * @see javax.mail.Part + * @author John Mani + */ + +public interface MimePart extends Part { + + /** + * Get the values of all header fields available for this header, + * returned as a single String, with the values separated by the + * delimiter. If the delimiter is null, only the + * first value is returned. + * + * @param name the name of this header + * @param delimiter delimiter between fields in returned string + * @return the value fields for all headers with + * this name + * @exception MessagingException for failures + */ + public String getHeader(String name, String delimiter) + throws MessagingException; + + /** + * Add a raw RFC822 header-line. + * + * @param line the line to add + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this Part is + * obtained from a READ_ONLY folder + * @exception MessagingException for other failures + */ + public void addHeaderLine(String line) throws MessagingException; + + /** + * Get all header lines as an Enumeration of Strings. A Header + * line is a raw RFC822 header-line, containing both the "name" + * and "value" field. + * + * @return an Enumeration of Strings + * @exception MessagingException for failures + */ + public Enumeration getAllHeaderLines() throws MessagingException; + + /** + * Get matching header lines as an Enumeration of Strings. + * A Header line is a raw RFC822 header-line, containing both + * the "name" and "value" field. + * + * @param names the headers to return + * @return an Enumeration of Strings + * @exception MessagingException for failures + */ + public Enumeration getMatchingHeaderLines(String[] names) + throws MessagingException; + + /** + * Get non-matching header lines as an Enumeration of Strings. + * A Header line is a raw RFC822 header-line, containing both + * the "name" and "value" field. + * + * @param names the headers to not return + * @return an Enumeration of Strings + * @exception MessagingException for failures + */ + public Enumeration getNonMatchingHeaderLines(String[] names) + throws MessagingException; + + /** + * Get the transfer encoding of this part. + * + * @return content-transfer-encoding + * @exception MessagingException for failures + */ + public String getEncoding() throws MessagingException; + + /** + * Get the Content-ID of this part. Returns null if none present. + * + * @return content-ID + * @exception MessagingException for failures + */ + public String getContentID() throws MessagingException; + + /** + * Get the Content-MD5 digest of this part. Returns null if + * none present. + * + * @return content-MD5 + * @exception MessagingException for failures + */ + public String getContentMD5() throws MessagingException; + + /** + * Set the Content-MD5 of this part. + * + * @param md5 the MD5 value + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this Part is + * obtained from a READ_ONLY folder + */ + public void setContentMD5(String md5) throws MessagingException; + + /** + * Get the language tags specified in the Content-Language header + * of this MimePart. The Content-Language header is defined by + * RFC 1766. Returns null if this header is not + * available. + * + * @return array of content language strings + * @exception MessagingException for failures + */ + public String[] getContentLanguage() throws MessagingException; + + /** + * Set the Content-Language header of this MimePart. The + * Content-Language header is defined by RFC1766. + * + * @param languages array of language tags + * @exception IllegalWriteException if the underlying + * implementation does not support modification + * @exception IllegalStateException if this Part is + * obtained from a READ_ONLY folder + */ + public void setContentLanguage(String[] languages) + throws MessagingException; + + /** + * Convenience method that sets the given String as this + * part's content, with a MIME type of "text/plain". If the + * string contains non US-ASCII characters. it will be encoded + * using the platform's default charset. The charset is also + * used to set the "charset" parameter.

    + * + * Note that there may be a performance penalty if + * text is large, since this method may have + * to scan all the characters to determine what charset to + * use.

    + * + * If the charset is already known, use the + * setText method that takes the charset parameter. + * + * @param text the text content to set + * @exception MessagingException if an error occurs + * @see #setText(String text, String charset) + */ + @Override + public void setText(String text) throws MessagingException; + + /** + * Convenience method that sets the given String as this part's + * content, with a MIME type of "text/plain" and the specified + * charset. The given Unicode string will be charset-encoded + * using the specified charset. The charset is also used to set + * "charset" parameter. + * + * @param text the text content to set + * @param charset the charset to use for the text + * @exception MessagingException if an error occurs + */ + public void setText(String text, String charset) + throws MessagingException; + + /** + * Convenience method that sets the given String as this part's + * content, with a primary MIME type of "text" and the specified + * MIME subtype. The given Unicode string will be charset-encoded + * using the specified charset. The charset is also used to set + * the "charset" parameter. + * + * @param text the text content to set + * @param charset the charset to use for the text + * @param subtype the MIME subtype to use (e.g., "html") + * @exception MessagingException if an error occurs + * @since JavaMail 1.4 + */ + public void setText(String text, String charset, String subtype) + throws MessagingException; +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/MimePartDataSource.java b/fine-third-default/fine-mail/src/javax/mail/internet/MimePartDataSource.java new file mode 100644 index 000000000..90236e0f6 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/MimePartDataSource.java @@ -0,0 +1,177 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import javax.mail.*; +import javax.activation.*; +import java.io.*; +import java.net.UnknownServiceException; +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.FolderClosedIOException; + +/** + * A utility class that implements a DataSource out of + * a MimePart. This class is primarily meant for service providers. + * + * @see javax.mail.internet.MimePart + * @see javax.activation.DataSource + * @author John Mani + */ + +public class MimePartDataSource implements DataSource, MessageAware { + /** + * The MimePart that provides the data for this DataSource. + * + * @since JavaMail 1.4 + */ + protected MimePart part; + + private MessageContext context; + + /** + * Constructor, that constructs a DataSource from a MimePart. + * + * @param part the MimePart + */ + public MimePartDataSource(MimePart part) { + this.part = part; + } + + /** + * Returns an input stream from this MimePart.

    + * + * This method applies the appropriate transfer-decoding, based + * on the Content-Transfer-Encoding attribute of this MimePart. + * Thus the returned input stream is a decoded stream of bytes.

    + * + * This implementation obtains the raw content from the Part + * using the getContentStream() method and decodes + * it using the MimeUtility.decode() method. + * + * @see javax.mail.internet.MimeMessage#getContentStream + * @see javax.mail.internet.MimeBodyPart#getContentStream + * @see javax.mail.internet.MimeUtility#decode + * @return decoded input stream + */ + @Override + public InputStream getInputStream() throws IOException { + InputStream is; + + try { + if (part instanceof MimeBodyPart) + is = ((MimeBodyPart)part).getContentStream(); + else if (part instanceof MimeMessage) + is = ((MimeMessage)part).getContentStream(); + else + throw new MessagingException("Unknown part"); + + String encoding = + MimeBodyPart.restrictEncoding(part, part.getEncoding()); + if (encoding != null) + return MimeUtility.decode(is, encoding); + else + return is; + } catch (FolderClosedException fex) { + throw new FolderClosedIOException(fex.getFolder(), + fex.getMessage()); + } catch (MessagingException mex) { + IOException ioex = new IOException(mex.getMessage()); + ioex.initCause(mex); + throw ioex; + } + } + + /** + * DataSource method to return an output stream.

    + * + * This implementation throws the UnknownServiceException. + */ + @Override + public OutputStream getOutputStream() throws IOException { + throw new UnknownServiceException("Writing not supported"); + } + + /** + * Returns the content-type of this DataSource.

    + * + * This implementation just invokes the getContentType + * method on the MimePart. + */ + @Override + public String getContentType() { + try { + return part.getContentType(); + } catch (MessagingException mex) { + // would like to be able to reflect the exception to the + // application, but since we can't do that we return a + // generic "unknown" value here and hope for another + // exception later. + return "application/octet-stream"; + } + } + + /** + * DataSource method to return a name.

    + * + * This implementation just returns an empty string. + */ + @Override + public String getName() { + try { + if (part instanceof MimeBodyPart) + return ((MimeBodyPart)part).getFileName(); + } catch (MessagingException mex) { + // ignore it + } + return ""; + } + + /** + * Return the MessageContext for the current part. + * @since JavaMail 1.1 + */ + @Override + public synchronized MessageContext getMessageContext() { + if (context == null) + context = new MessageContext(part); + return context; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/MimeUtility.java b/fine-third-default/fine-mail/src/javax/mail/internet/MimeUtility.java new file mode 100644 index 000000000..50e839c34 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/MimeUtility.java @@ -0,0 +1,1732 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import javax.mail.MessagingException; +import javax.mail.EncodingAware; +import javax.activation.*; +import java.util.*; +import java.io.*; +import java.nio.charset.Charset; +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.ASCIIUtility; +import com.sun.mail.util.BASE64DecoderStream; +import com.sun.mail.util.BASE64EncoderStream; +import com.sun.mail.util.BEncoderStream; +import com.sun.mail.util.LineInputStream; +import com.sun.mail.util.LineOutputStream; +import com.sun.mail.util.LogOutputStream; +import com.sun.mail.util.QDecoderStream; +import com.sun.mail.util.QEncoderStream; +import com.sun.mail.util.QPDecoderStream; +import com.sun.mail.util.QPEncoderStream; +import com.sun.mail.util.UUDecoderStream; +import com.sun.mail.util.UUEncoderStream; + +/** + * This is a utility class that provides various MIME related + * functionality.

    + * + * There are a set of methods to encode and decode MIME headers as + * per RFC 2047. Note that, in general, these methods are + * not needed when using methods such as + * setSubject and setRecipients; JavaMail + * will automatically encode and decode data when using these "higher + * level" methods. The methods below are only needed when maniuplating + * raw MIME headers using setHeader and getHeader + * methods. A brief description on handling such headers is given below:

    + * + * RFC 822 mail headers must contain only US-ASCII + * characters. Headers that contain non US-ASCII characters must be + * encoded so that they contain only US-ASCII characters. Basically, + * this process involves using either BASE64 or QP to encode certain + * characters. RFC 2047 describes this in detail.

    + * + * In Java, Strings contain (16 bit) Unicode characters. ASCII is a + * subset of Unicode (and occupies the range 0 - 127). A String + * that contains only ASCII characters is already mail-safe. If the + * String contains non US-ASCII characters, it must be encoded. An + * additional complexity in this step is that since Unicode is not + * yet a widely used charset, one might want to first charset-encode + * the String into another charset and then do the transfer-encoding. + *

    + * Note that to get the actual bytes of a mail-safe String (say, + * for sending over SMTP), one must do + *

    + *
    + *	byte[] bytes = string.getBytes("iso-8859-1");	
    + *
    + * 

    + * + * The setHeader and addHeader methods + * on MimeMessage and MimeBodyPart assume that the given header values + * are Unicode strings that contain only US-ASCII characters. Hence + * the callers of those methods must insure that the values they pass + * do not contain non US-ASCII characters. The methods in this class + * help do this.

    + * + * The getHeader family of methods on MimeMessage and + * MimeBodyPart return the raw header value. These might be encoded + * as per RFC 2047, and if so, must be decoded into Unicode Strings. + * The methods in this class help to do this.

    + * + * Several System properties control strict conformance to the MIME + * spec. Note that these are not session properties but must be set + * globally as System properties.

    + * + * The mail.mime.decodetext.strict property controls + * decoding of MIME encoded words. The MIME spec requires that encoded + * words start at the beginning of a whitespace separated word. Some + * mailers incorrectly include encoded words in the middle of a word. + * If the mail.mime.decodetext.strict System property is + * set to "false", an attempt will be made to decode these + * illegal encoded words. The default is true.

    + * + * The mail.mime.encodeeol.strict property controls the + * choice of Content-Transfer-Encoding for MIME parts that are not of + * type "text". Often such parts will contain textual data for which + * an encoding that allows normal end of line conventions is appropriate. + * In rare cases, such a part will appear to contain entirely textual + * data, but will require an encoding that preserves CR and LF characters + * without change. If the mail.mime.encodeeol.strict + * System property is set to "true", such an encoding will + * be used when necessary. The default is false.

    + * + * In addition, the mail.mime.charset System property can + * be used to specify the default MIME charset to use for encoded words + * and text parts that don't otherwise specify a charset. Normally, the + * default MIME charset is derived from the default Java charset, as + * specified in the file.encoding System property. Most + * applications will have no need to explicitly set the default MIME + * charset. In cases where the default MIME charset to be used for + * mail messages is different than the charset used for files stored on + * the system, this property should be set.

    + * + * The current implementation also supports the following System property. + *

    + * The mail.mime.ignoreunknownencoding property controls + * whether unknown values in the Content-Transfer-Encoding + * header, as passed to the decode method, cause an exception. + * If set to "true", unknown values are ignored and 8bit + * encoding is assumed. Otherwise, unknown values cause a MessagingException + * to be thrown. + * + * @author John Mani + * @author Bill Shannon + */ + +public class MimeUtility { + + // This class cannot be instantiated + private MimeUtility() { } + + public static final int ALL = -1; + + // cached map of whether a charset is compatible with ASCII + // Map + private static final Map nonAsciiCharsetMap + = new HashMap<>(); + + private static final boolean decodeStrict = + PropUtil.getBooleanSystemProperty("mail.mime.decodetext.strict", true); + private static final boolean encodeEolStrict = + PropUtil.getBooleanSystemProperty("mail.mime.encodeeol.strict", false); + private static final boolean ignoreUnknownEncoding = + PropUtil.getBooleanSystemProperty( + "mail.mime.ignoreunknownencoding", false); + private static final boolean allowUtf8 = + PropUtil.getBooleanSystemProperty("mail.mime.allowutf8", false); + /* + * The following two properties allow disabling the fold() + * and unfold() methods and reverting to the previous behavior. + * They should never need to be changed and are here only because + * of my paranoid concern with compatibility. + */ + private static final boolean foldEncodedWords = + PropUtil.getBooleanSystemProperty("mail.mime.foldencodedwords", false); + private static final boolean foldText = + PropUtil.getBooleanSystemProperty("mail.mime.foldtext", true); + + + /** + * Get the Content-Transfer-Encoding that should be applied + * to the input stream of this DataSource, to make it mail-safe.

    + * + * The algorithm used here is:
    + *

      + *
    • + * If the DataSource implements {@link EncodingAware}, ask it + * what encoding to use. If it returns non-null, return that value. + *
    • + * If the primary type of this datasource is "text" and if all + * the bytes in its input stream are US-ASCII, then the encoding + * is "7bit". If more than half of the bytes are non-US-ASCII, then + * the encoding is "base64". If less than half of the bytes are + * non-US-ASCII, then the encoding is "quoted-printable". + *
    • + * If the primary type of this datasource is not "text", then if + * all the bytes of its input stream are US-ASCII, the encoding + * is "7bit". If there is even one non-US-ASCII character, the + * encoding is "base64". + *
    + * + * @param ds the DataSource + * @return the encoding. This is either "7bit", + * "quoted-printable" or "base64" + */ + public static String getEncoding(DataSource ds) { + ContentType cType = null; + InputStream is = null; + String encoding = null; + + if (ds instanceof EncodingAware) { + encoding = ((EncodingAware)ds).getEncoding(); + if (encoding != null) + return encoding; + } + try { + cType = new ContentType(ds.getContentType()); + is = ds.getInputStream(); + + boolean isText = cType.match("text/*"); + // if not text, stop processing when we see non-ASCII + int i = checkAscii(is, ALL, !isText); + switch (i) { + case ALL_ASCII: + encoding = "7bit"; // all ASCII + break; + case MOSTLY_ASCII: + if (isText && nonAsciiCharset(cType)) + encoding = "base64"; // charset isn't compatible with ASCII + else + encoding = "quoted-printable"; // mostly ASCII + break; + default: + encoding = "base64"; // mostly binary + break; + } + + } catch (Exception ex) { + return "base64"; // what else ?! + } finally { + // Close the input stream + try { + if (is != null) + is.close(); + } catch (IOException ioex) { } + } + + return encoding; + } + + /** + * Determine whether the charset in the Content-Type is compatible + * with ASCII or not. A charset is compatible with ASCII if the + * encoded byte stream representing the Unicode string "\r\n" is + * the ASCII characters CR and LF. For example, the utf-16be + * charset is not compatible with ASCII. + * + * For performance, we keep a static map that caches the results. + */ + private static boolean nonAsciiCharset(ContentType ct) { + String charset = ct.getParameter("charset"); + if (charset == null) + return false; + charset = charset.toLowerCase(Locale.ENGLISH); + Boolean bool; + synchronized (nonAsciiCharsetMap) { + bool = nonAsciiCharsetMap.get(charset); + } + if (bool == null) { + try { + byte[] b = "\r\n".getBytes(charset); + bool = Boolean.valueOf( + b.length != 2 || b[0] != 015 || b[1] != 012); + } catch (UnsupportedEncodingException uex) { + bool = Boolean.FALSE; // a guess + } catch (RuntimeException ex) { + bool = Boolean.TRUE; // one of the weird ones? + } + synchronized (nonAsciiCharsetMap) { + nonAsciiCharsetMap.put(charset, bool); + } + } + return bool.booleanValue(); + } + + /** + * Same as getEncoding(DataSource) except that instead + * of reading the data from an InputStream it uses the + * writeTo method to examine the data. This is more + * efficient in the common case of a DataHandler + * created with an object and a MIME type (for example, a + * "text/plain" String) because all the I/O is done in this + * thread. In the case requiring an InputStream the + * DataHandler uses a thread, a pair of pipe streams, + * and the writeTo method to produce the data.

    + * + * @param dh the DataHandler + * @return the Content-Transfer-Encoding + * @since JavaMail 1.2 + */ + public static String getEncoding(DataHandler dh) { + ContentType cType = null; + String encoding = null; + + /* + * Try to pick the most efficient means of determining the + * encoding. If this DataHandler was created using a DataSource, + * the getEncoding(DataSource) method is typically faster. If + * the DataHandler was created with an object, this method is + * much faster. To distinguish the two cases, we use a heuristic. + * A DataHandler created with an object will always have a null name. + * A DataHandler created with a DataSource will usually have a + * non-null name. + * + * XXX - This is actually quite a disgusting hack, but it makes + * a common case run over twice as fast. + */ + if (dh.getName() != null) + return getEncoding(dh.getDataSource()); + + try { + cType = new ContentType(dh.getContentType()); + } catch (Exception ex) { + return "base64"; // what else ?! + } + + if (cType.match("text/*")) { + // Check all of the available bytes + AsciiOutputStream aos = new AsciiOutputStream(false, false); + try { + dh.writeTo(aos); + } catch (IOException ex) { + // ignore it, can't happen + } + switch (aos.getAscii()) { + case ALL_ASCII: + encoding = "7bit"; // all ascii + break; + case MOSTLY_ASCII: + encoding = "quoted-printable"; // mostly ascii + break; + default: + encoding = "base64"; // mostly binary + break; + } + } else { // not "text" + // Check all of available bytes, break out if we find + // at least one non-US-ASCII character + AsciiOutputStream aos = + new AsciiOutputStream(true, encodeEolStrict); + try { + dh.writeTo(aos); + } catch (IOException ex) { } // ignore it + if (aos.getAscii() == ALL_ASCII) // all ascii + encoding = "7bit"; + else // found atleast one non-ascii character, use b64 + encoding = "base64"; + } + + return encoding; + } + + /** + * Decode the given input stream. The Input stream returned is + * the decoded input stream. All the encodings defined in RFC 2045 + * are supported here. They include "base64", "quoted-printable", + * "7bit", "8bit", and "binary". In addition, "uuencode" is also + * supported.

    + * + * In the current implementation, if the + * mail.mime.ignoreunknownencoding system property is set to + * "true", unknown encoding values are ignored and the + * original InputStream is returned. + * + * @param is input stream + * @param encoding the encoding of the stream. + * @return decoded input stream. + * @exception MessagingException if the encoding is unknown + */ + public static InputStream decode(InputStream is, String encoding) + throws MessagingException { + if (encoding.equalsIgnoreCase("base64")) + return new BASE64DecoderStream(is); + else if (encoding.equalsIgnoreCase("quoted-printable")) + return new QPDecoderStream(is); + else if (encoding.equalsIgnoreCase("uuencode") || + encoding.equalsIgnoreCase("x-uuencode") || + encoding.equalsIgnoreCase("x-uue")) + return new UUDecoderStream(is); + else if (encoding.equalsIgnoreCase("binary") || + encoding.equalsIgnoreCase("7bit") || + encoding.equalsIgnoreCase("8bit")) + return is; + else { + if (!ignoreUnknownEncoding) + throw new MessagingException("Unknown encoding: " + encoding); + return is; + } + } + + /** + * Wrap an encoder around the given output stream. + * All the encodings defined in RFC 2045 are supported here. + * They include "base64", "quoted-printable", "7bit", "8bit" and + * "binary". In addition, "uuencode" is also supported. + * + * @param os output stream + * @param encoding the encoding of the stream. + * @return output stream that applies the + * specified encoding. + * @exception MessagingException if the encoding is unknown + */ + public static OutputStream encode(OutputStream os, String encoding) + throws MessagingException { + if (encoding == null) + return os; + else if (encoding.equalsIgnoreCase("base64")) + return new BASE64EncoderStream(os); + else if (encoding.equalsIgnoreCase("quoted-printable")) + return new QPEncoderStream(os); + else if (encoding.equalsIgnoreCase("uuencode") || + encoding.equalsIgnoreCase("x-uuencode") || + encoding.equalsIgnoreCase("x-uue")) + return new UUEncoderStream(os); + else if (encoding.equalsIgnoreCase("binary") || + encoding.equalsIgnoreCase("7bit") || + encoding.equalsIgnoreCase("8bit")) + return os; + else + throw new MessagingException("Unknown encoding: " +encoding); + } + + /** + * Wrap an encoder around the given output stream. + * All the encodings defined in RFC 2045 are supported here. + * They include "base64", "quoted-printable", "7bit", "8bit" and + * "binary". In addition, "uuencode" is also supported. + * The filename parameter is used with the "uuencode" + * encoding and is included in the encoded output. + * + * @param os output stream + * @param encoding the encoding of the stream. + * @param filename name for the file being encoded (only used + * with uuencode) + * @return output stream that applies the + * specified encoding. + * @exception MessagingException for unknown encodings + * @since JavaMail 1.2 + */ + public static OutputStream encode(OutputStream os, String encoding, + String filename) + throws MessagingException { + if (encoding == null) + return os; + else if (encoding.equalsIgnoreCase("base64")) + return new BASE64EncoderStream(os); + else if (encoding.equalsIgnoreCase("quoted-printable")) + return new QPEncoderStream(os); + else if (encoding.equalsIgnoreCase("uuencode") || + encoding.equalsIgnoreCase("x-uuencode") || + encoding.equalsIgnoreCase("x-uue")) + return new UUEncoderStream(os, filename); + else if (encoding.equalsIgnoreCase("binary") || + encoding.equalsIgnoreCase("7bit") || + encoding.equalsIgnoreCase("8bit")) + return os; + else + throw new MessagingException("Unknown encoding: " +encoding); + } + + /** + * Encode a RFC 822 "text" token into mail-safe form as per + * RFC 2047.

    + * + * The given Unicode string is examined for non US-ASCII + * characters. If the string contains only US-ASCII characters, + * it is returned as-is. If the string contains non US-ASCII + * characters, it is first character-encoded using the platform's + * default charset, then transfer-encoded using either the B or + * Q encoding. The resulting bytes are then returned as a Unicode + * string containing only ASCII characters.

    + * + * Note that this method should be used to encode only + * "unstructured" RFC 822 headers.

    + * + * Example of usage: + *

    +     *
    +     *  MimePart part = ...
    +     *  String rawvalue = "FooBar Mailer, Japanese version 1.1"
    +     *  try {
    +     *    // If we know for sure that rawvalue contains only US-ASCII 
    +     *    // characters, we can skip the encoding part
    +     *    part.setHeader("X-mailer", MimeUtility.encodeText(rawvalue));
    +     *  } catch (UnsupportedEncodingException e) {
    +     *    // encoding failure
    +     *  } catch (MessagingException me) {
    +     *   // setHeader() failure
    +     *  }
    +     *
    +     * 

    + * + * @param text Unicode string + * @return Unicode string containing only US-ASCII characters + * @exception UnsupportedEncodingException if the encoding fails + */ + public static String encodeText(String text) + throws UnsupportedEncodingException { + return encodeText(text, null, null); + } + + /** + * Encode a RFC 822 "text" token into mail-safe form as per + * RFC 2047.

    + * + * The given Unicode string is examined for non US-ASCII + * characters. If the string contains only US-ASCII characters, + * it is returned as-is. If the string contains non US-ASCII + * characters, it is first character-encoded using the specified + * charset, then transfer-encoded using either the B or Q encoding. + * The resulting bytes are then returned as a Unicode string + * containing only ASCII characters.

    + * + * Note that this method should be used to encode only + * "unstructured" RFC 822 headers. + * + * @param text the header value + * @param charset the charset. If this parameter is null, the + * platform's default chatset is used. + * @param encoding the encoding to be used. Currently supported + * values are "B" and "Q". If this parameter is null, then + * the "Q" encoding is used if most of characters to be + * encoded are in the ASCII charset, otherwise "B" encoding + * is used. + * @return Unicode string containing only US-ASCII characters + * @exception UnsupportedEncodingException if the charset + * conversion failed. + */ + public static String encodeText(String text, String charset, + String encoding) + throws UnsupportedEncodingException { + return encodeWord(text, charset, encoding, false); + } + + /** + * Decode "unstructured" headers, that is, headers that are defined + * as '*text' as per RFC 822.

    + * + * The string is decoded using the algorithm specified in + * RFC 2047, Section 6.1. If the charset-conversion fails + * for any sequence, an UnsupportedEncodingException is thrown. + * If the String is not an RFC 2047 style encoded header, it is + * returned as-is

    + * + * Example of usage: + *

    +     *
    +     *  MimePart part = ...
    +     *  String rawvalue = null;
    +     *  String  value = null;
    +     *  try {
    +     *    if ((rawvalue = part.getHeader("X-mailer")[0]) != null)
    +     *      value = MimeUtility.decodeText(rawvalue);
    +     *  } catch (UnsupportedEncodingException e) {
    +     *      // Don't care
    +     *      value = rawvalue;
    +     *  } catch (MessagingException me) { }
    +     *
    +     *  return value;
    +     *
    +     * 

    + * + * @param etext the possibly encoded value + * @return the decoded text + * @exception UnsupportedEncodingException if the charset + * conversion failed. + */ + public static String decodeText(String etext) + throws UnsupportedEncodingException { + /* + * We look for sequences separated by "linear-white-space". + * (as per RFC 2047, Section 6.1) + * RFC 822 defines "linear-white-space" as SPACE | HT | CR | NL. + */ + String lwsp = " \t\n\r"; + StringTokenizer st; + + /* + * First, lets do a quick run thru the string and check + * whether the sequence "=?" exists at all. If none exists, + * we know there are no encoded-words in here and we can just + * return the string as-is, without suffering thru the later + * decoding logic. + * This handles the most common case of unencoded headers + * efficiently. + */ + if (etext.indexOf("=?") == -1) + return etext; + + // Encoded words found. Start decoding ... + + st = new StringTokenizer(etext, lwsp, true); + StringBuilder sb = new StringBuilder(); // decode buffer + StringBuilder wsb = new StringBuilder(); // white space buffer + boolean prevWasEncoded = false; + + while (st.hasMoreTokens()) { + char c; + String s = st.nextToken(); + // If whitespace, append it to the whitespace buffer + if (((c = s.charAt(0)) == ' ') || (c == '\t') || + (c == '\r') || (c == '\n')) + wsb.append(c); + else { + // Check if token is an 'encoded-word' .. + String word; + try { + word = decodeWord(s); + // Yes, this IS an 'encoded-word'. + if (!prevWasEncoded && wsb.length() > 0) { + // if the previous word was also encoded, we + // should ignore the collected whitespace. Else + // we include the whitespace as well. + sb.append(wsb); + } + prevWasEncoded = true; + } catch (ParseException pex) { + // This is NOT an 'encoded-word'. + word = s; + // possibly decode inner encoded words + if (!decodeStrict) { + String dword = decodeInnerWords(word); + if (dword != word) { + // if a different String object was returned, + // decoding was done. + if (prevWasEncoded && word.startsWith("=?")) { + // encoded followed by encoded, + // throw away whitespace between + } else { + // include collected whitespace .. + if (wsb.length() > 0) + sb.append(wsb); + } + // did original end with encoded? + prevWasEncoded = word.endsWith("?="); + word = dword; + } else { + // include collected whitespace .. + if (wsb.length() > 0) + sb.append(wsb); + prevWasEncoded = false; + } + } else { + // include collected whitespace .. + if (wsb.length() > 0) + sb.append(wsb); + prevWasEncoded = false; + } + } + sb.append(word); // append the actual word + wsb.setLength(0); // reset wsb for reuse + } + } + sb.append(wsb); // append trailing whitespace + return sb.toString(); + } + + /** + * Encode a RFC 822 "word" token into mail-safe form as per + * RFC 2047.

    + * + * The given Unicode string is examined for non US-ASCII + * characters. If the string contains only US-ASCII characters, + * it is returned as-is. If the string contains non US-ASCII + * characters, it is first character-encoded using the platform's + * default charset, then transfer-encoded using either the B or + * Q encoding. The resulting bytes are then returned as a Unicode + * string containing only ASCII characters.

    + * + * This method is meant to be used when creating RFC 822 "phrases". + * The InternetAddress class, for example, uses this to encode + * it's 'phrase' component. + * + * @param word Unicode string + * @return Array of Unicode strings containing only US-ASCII + * characters. + * @exception UnsupportedEncodingException if the encoding fails + */ + public static String encodeWord(String word) + throws UnsupportedEncodingException { + return encodeWord(word, null, null); + } + + /** + * Encode a RFC 822 "word" token into mail-safe form as per + * RFC 2047.

    + * + * The given Unicode string is examined for non US-ASCII + * characters. If the string contains only US-ASCII characters, + * it is returned as-is. If the string contains non US-ASCII + * characters, it is first character-encoded using the specified + * charset, then transfer-encoded using either the B or Q encoding. + * The resulting bytes are then returned as a Unicode string + * containing only ASCII characters.

    + * + * @param word Unicode string + * @param charset the MIME charset + * @param encoding the encoding to be used. Currently supported + * values are "B" and "Q". If this parameter is null, then + * the "Q" encoding is used if most of characters to be + * encoded are in the ASCII charset, otherwise "B" encoding + * is used. + * @return Unicode string containing only US-ASCII characters + * @exception UnsupportedEncodingException if the encoding fails + */ + public static String encodeWord(String word, String charset, + String encoding) + throws UnsupportedEncodingException { + return encodeWord(word, charset, encoding, true); + } + + /* + * Encode the given string. The parameter 'encodingWord' should + * be true if a RFC 822 "word" token is being encoded and false if a + * RFC 822 "text" token is being encoded. This is because the + * "Q" encoding defined in RFC 2047 has more restrictions when + * encoding "word" tokens. (Sigh) + */ + private static String encodeWord(String string, String charset, + String encoding, boolean encodingWord) + throws UnsupportedEncodingException { + + // If 'string' contains only US-ASCII characters, just + // return it. + int ascii = checkAscii(string); + if (ascii == ALL_ASCII) + return string; + + // Else, apply the specified charset conversion. + String jcharset; + if (charset == null) { // use default charset + jcharset = getDefaultJavaCharset(); // the java charset + charset = getDefaultMIMECharset(); // the MIME equivalent + } else // MIME charset -> java charset + jcharset = javaCharset(charset); + + // If no transfer-encoding is specified, figure one out. + if (encoding == null) { + if (ascii != MOSTLY_NONASCII) + encoding = "Q"; + else + encoding = "B"; + } + + boolean b64; + if (encoding.equalsIgnoreCase("B")) + b64 = true; + else if (encoding.equalsIgnoreCase("Q")) + b64 = false; + else + throw new UnsupportedEncodingException( + "Unknown transfer encoding: " + encoding); + + StringBuilder outb = new StringBuilder(); // the output buffer + doEncode(string, b64, jcharset, + // As per RFC 2047, size of an encoded string should not + // exceed 75 bytes. + // 7 = size of "=?", '?', 'B'/'Q', '?', "?=" + 75 - 7 - charset.length(), // the available space + "=?" + charset + "?" + encoding + "?", // prefix + true, encodingWord, outb); + + return outb.toString(); + } + + private static void doEncode(String string, boolean b64, + String jcharset, int avail, String prefix, + boolean first, boolean encodingWord, StringBuilder buf) + throws UnsupportedEncodingException { + + // First find out what the length of the encoded version of + // 'string' would be. + byte[] bytes = string.getBytes(jcharset); + int len; + if (b64) // "B" encoding + len = BEncoderStream.encodedLength(bytes); + else // "Q" + len = QEncoderStream.encodedLength(bytes, encodingWord); + + int size; + if ((len > avail) && ((size = string.length()) > 1)) { + // If the length is greater than 'avail', split 'string' + // into two and recurse. + // Have to make sure not to split a Unicode surrogate pair. + int split = size / 2; + if (Character.isHighSurrogate(string.charAt(split-1))) + split--; + if (split > 0) + doEncode(string.substring(0, split), b64, jcharset, + avail, prefix, first, encodingWord, buf); + doEncode(string.substring(split, size), b64, jcharset, + avail, prefix, false, encodingWord, buf); + } else { + // length <= than 'avail'. Encode the given string + ByteArrayOutputStream os = new ByteArrayOutputStream(); + OutputStream eos; // the encoder + if (b64) // "B" encoding + eos = new BEncoderStream(os); + else // "Q" encoding + eos = new QEncoderStream(os, encodingWord); + + try { // do the encoding + eos.write(bytes); + eos.close(); + } catch (IOException ioex) { } + + byte[] encodedBytes = os.toByteArray(); // the encoded stuff + // Now write out the encoded (all ASCII) bytes into our + // StringBuilder + if (!first) // not the first line of this sequence + if (foldEncodedWords) + buf.append("\r\n "); // start a continuation line + else + buf.append(" "); // line will be folded later + + buf.append(prefix); + for (int i = 0; i < encodedBytes.length; i++) + buf.append((char)encodedBytes[i]); + buf.append("?="); // terminate the current sequence + } + } + + /** + * The string is parsed using the rules in RFC 2047 and RFC 2231 for + * parsing an "encoded-word". If the parse fails, a ParseException is + * thrown. Otherwise, it is transfer-decoded, and then + * charset-converted into Unicode. If the charset-conversion + * fails, an UnsupportedEncodingException is thrown.

    + * + * @param eword the encoded value + * @return the decoded word + * @exception ParseException if the string is not an + * encoded-word as per RFC 2047 and RFC 2231. + * @exception UnsupportedEncodingException if the charset + * conversion failed. + */ + public static String decodeWord(String eword) + throws ParseException, UnsupportedEncodingException { + + if (!eword.startsWith("=?")) // not an encoded word + throw new ParseException( + "encoded word does not start with \"=?\": " + eword); + + // get charset + int start = 2; int pos; + if ((pos = eword.indexOf('?', start)) == -1) + throw new ParseException( + "encoded word does not include charset: " + eword); + String charset = eword.substring(start, pos); + int lpos = charset.indexOf('*'); // RFC 2231 language specified? + if (lpos >= 0) // yes, throw it away + charset = charset.substring(0, lpos); + charset = javaCharset(charset); + + // get encoding + start = pos+1; + if ((pos = eword.indexOf('?', start)) == -1) + throw new ParseException( + "encoded word does not include encoding: " + eword); + String encoding = eword.substring(start, pos); + + // get encoded-sequence + start = pos+1; + if ((pos = eword.indexOf("?=", start)) == -1) + throw new ParseException( + "encoded word does not end with \"?=\": " + eword); + /* + * XXX - should include this, but leaving it out for compatibility... + * + if (decodeStrict && pos != eword.length() - 2) + throw new ParseException( + "encoded word does not end with \"?=\": " + eword);); + */ + String word = eword.substring(start, pos); + + try { + String decodedWord; + if (word.length() > 0) { + // Extract the bytes from word + ByteArrayInputStream bis = + new ByteArrayInputStream(ASCIIUtility.getBytes(word)); + + // Get the appropriate decoder + InputStream is; + if (encoding.equalsIgnoreCase("B")) + is = new BASE64DecoderStream(bis); + else if (encoding.equalsIgnoreCase("Q")) + is = new QDecoderStream(bis); + else + throw new UnsupportedEncodingException( + "unknown encoding: " + encoding); + + // For b64 & q, size of decoded word <= size of word. So + // the decoded bytes must fit into the 'bytes' array. This + // is certainly more efficient than writing bytes into a + // ByteArrayOutputStream and then pulling out the byte[] + // from it. + int count = bis.available(); + byte[] bytes = new byte[count]; + // count is set to the actual number of decoded bytes + count = is.read(bytes, 0, count); + + // Finally, convert the decoded bytes into a String using + // the specified charset + decodedWord = count <= 0 ? "" : + new String(bytes, 0, count, charset); + } else { + // no characters to decode, return empty string + decodedWord = ""; + } + if (pos + 2 < eword.length()) { + // there's still more text in the string + String rest = eword.substring(pos + 2); + if (!decodeStrict) + rest = decodeInnerWords(rest); + decodedWord += rest; + } + return decodedWord; + } catch (UnsupportedEncodingException uex) { + // explicitly catch and rethrow this exception, otherwise + // the below IOException catch will swallow this up! + throw uex; + } catch (IOException ioex) { + // Shouldn't happen. + throw new ParseException(ioex.toString()); + } catch (IllegalArgumentException iex) { + /* An unknown charset of the form ISO-XXX-XXX, will cause + * the JDK to throw an IllegalArgumentException ... Since the + * JDK will attempt to create a classname using this string, + * but valid classnames must not contain the character '-', + * and this results in an IllegalArgumentException, rather than + * the expected UnsupportedEncodingException. Yikes + */ + throw new UnsupportedEncodingException(charset); + } + } + + /** + * Look for encoded words within a word. The MIME spec doesn't + * allow this, but many broken mailers, especially Japanese mailers, + * produce such incorrect encodings. + */ + private static String decodeInnerWords(String word) + throws UnsupportedEncodingException { + int start = 0, i; + StringBuilder buf = new StringBuilder(); + while ((i = word.indexOf("=?", start)) >= 0) { + buf.append(word.substring(start, i)); + // find first '?' after opening '=?' - end of charset + int end = word.indexOf('?', i + 2); + if (end < 0) + break; + // find next '?' after that - end of encoding + end = word.indexOf('?', end + 1); + if (end < 0) + break; + // find terminating '?=' + end = word.indexOf("?=", end + 1); + if (end < 0) + break; + String s = word.substring(i, end + 2); + try { + s = decodeWord(s); + } catch (ParseException pex) { + // ignore it, just use the original string + } + buf.append(s); + start = end + 2; + } + if (start == 0) + return word; + if (start < word.length()) + buf.append(word.substring(start)); + return buf.toString(); + } + + /** + * A utility method to quote a word, if the word contains any + * characters from the specified 'specials' list.

    + * + * The HeaderTokenizer class defines two special + * sets of delimiters - MIME and RFC 822.

    + * + * This method is typically used during the generation of + * RFC 822 and MIME header fields. + * + * @param word word to be quoted + * @param specials the set of special characters + * @return the possibly quoted word + * @see javax.mail.internet.HeaderTokenizer#MIME + * @see javax.mail.internet.HeaderTokenizer#RFC822 + */ + public static String quote(String word, String specials) { + int len = word == null ? 0 : word.length(); + if (len == 0) + return "\"\""; // an empty string is handled specially + + /* + * Look for any "bad" characters, Escape and + * quote the entire string if necessary. + */ + boolean needQuoting = false; + for (int i = 0; i < len; i++) { + char c = word.charAt(i); + if (c == '"' || c == '\\' || c == '\r' || c == '\n') { + // need to escape them and then quote the whole string + StringBuilder sb = new StringBuilder(len + 3); + sb.append('"'); + sb.append(word.substring(0, i)); + int lastc = 0; + for (int j = i; j < len; j++) { + char cc = word.charAt(j); + if ((cc == '"') || (cc == '\\') || + (cc == '\r') || (cc == '\n')) + if (cc == '\n' && lastc == '\r') + ; // do nothing, CR was already escaped + else + sb.append('\\'); // Escape the character + sb.append(cc); + lastc = cc; + } + sb.append('"'); + return sb.toString(); + } else if (c < 040 || (c >= 0177 && !allowUtf8) || + specials.indexOf(c) >= 0) + // These characters cause the string to be quoted + needQuoting = true; + } + + if (needQuoting) { + StringBuilder sb = new StringBuilder(len + 2); + sb.append('"').append(word).append('"'); + return sb.toString(); + } else + return word; + } + + /** + * Fold a string at linear whitespace so that each line is no longer + * than 76 characters, if possible. If there are more than 76 + * non-whitespace characters consecutively, the string is folded at + * the first whitespace after that sequence. The parameter + * used indicates how many characters have been used in + * the current line; it is usually the length of the header name.

    + * + * Note that line breaks in the string aren't escaped; they probably + * should be. + * + * @param used characters used in line so far + * @param s the string to fold + * @return the folded string + * @since JavaMail 1.4 + */ + public static String fold(int used, String s) { + if (!foldText) + return s; + + int end; + char c; + // Strip trailing spaces and newlines + for (end = s.length() - 1; end >= 0; end--) { + c = s.charAt(end); + if (c != ' ' && c != '\t' && c != '\r' && c != '\n') + break; + } + if (end != s.length() - 1) + s = s.substring(0, end + 1); + + // if the string fits now, just return it + if (used + s.length() <= 76) + return makesafe(s); + + // have to actually fold the string + StringBuilder sb = new StringBuilder(s.length() + 4); + char lastc = 0; + while (used + s.length() > 76) { + int lastspace = -1; + for (int i = 0; i < s.length(); i++) { + if (lastspace != -1 && used + i > 76) + break; + c = s.charAt(i); + if (c == ' ' || c == '\t') + if (!(lastc == ' ' || lastc == '\t')) + lastspace = i; + lastc = c; + } + if (lastspace == -1) { + // no space, use the whole thing + sb.append(s); + s = ""; + used = 0; + break; + } + sb.append(s.substring(0, lastspace)); + sb.append("\r\n"); + lastc = s.charAt(lastspace); + sb.append(lastc); + s = s.substring(lastspace + 1); + used = 1; + } + sb.append(s); + return makesafe(sb); + } + + /** + * If the String or StringBuilder has any embedded newlines, + * make sure they're followed by whitespace, to prevent header + * injection errors. + */ + private static String makesafe(CharSequence s) { + int i; + for (i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\r' || c == '\n') + break; + } + if (i == s.length()) // went through whole string with no CR or LF + return s.toString(); + + // read the lines in the string and reassemble them, + // eliminating blank lines and inserting whitespace as necessary + StringBuilder sb = new StringBuilder(s.length() + 1); + BufferedReader r = new BufferedReader(new StringReader(s.toString())); + String line; + try { + while ((line = r.readLine()) != null) { + if (line.trim().length() == 0) + continue; // ignore empty lines + if (sb.length() > 0) { + sb.append("\r\n"); + assert line.length() > 0; // proven above + char c = line.charAt(0); + if (c != ' ' && c != '\t') + sb.append(' '); + } + sb.append(line); + } + } catch (IOException ex) { + // XXX - should never happen when reading from a string + return s.toString(); + } + return sb.toString(); + } + + /** + * Unfold a folded header. Any line breaks that aren't escaped and + * are followed by whitespace are removed. + * + * @param s the string to unfold + * @return the unfolded string + * @since JavaMail 1.4 + */ + public static String unfold(String s) { + if (!foldText) + return s; + + StringBuilder sb = null; + int i; + while ((i = indexOfAny(s, "\r\n")) >= 0) { + int start = i; + int slen = s.length(); + i++; // skip CR or NL + if (i < slen && s.charAt(i - 1) == '\r' && s.charAt(i) == '\n') + i++; // skip LF + if (start > 0 && s.charAt(start - 1) == '\\') { + // there's a backslash before the line break + // strip it out, but leave in the line break + if (sb == null) + sb = new StringBuilder(s.length()); + sb.append(s.substring(0, start - 1)); + sb.append(s.substring(start, i)); + s = s.substring(i); + } else { + char c; + // if next line starts with whitespace, + // or at the end of the string, remove the line break + // XXX - next line should always start with whitespace + if (i >= slen || (c = s.charAt(i)) == ' ' || c == '\t') { + if (sb == null) + sb = new StringBuilder(s.length()); + sb.append(s.substring(0, start)); + s = s.substring(i); + } else { + // it's not a continuation line, just leave in the newline + if (sb == null) + sb = new StringBuilder(s.length()); + sb.append(s.substring(0, i)); + s = s.substring(i); + } + } + } + if (sb != null) { + sb.append(s); + return sb.toString(); + } else + return s; + } + + /** + * Return the first index of any of the characters in "any" in "s", + * or -1 if none are found. + * + * This should be a method on String. + */ + private static int indexOfAny(String s, String any) { + return indexOfAny(s, any, 0); + } + + private static int indexOfAny(String s, String any, int start) { + try { + int len = s.length(); + for (int i = start; i < len; i++) { + if (any.indexOf(s.charAt(i)) >= 0) + return i; + } + return -1; + } catch (StringIndexOutOfBoundsException e) { + return -1; + } + } + + /** + * Convert a MIME charset name into a valid Java charset name.

    + * + * @param charset the MIME charset name + * @return the Java charset equivalent. If a suitable mapping is + * not available, the passed in charset is itself returned. + */ + public static String javaCharset(String charset) { + if (mime2java == null || charset == null) + // no mapping table, or charset parameter is null + return charset; + + String alias = mime2java.get(charset.toLowerCase(Locale.ENGLISH)); + if (alias != null) { + // verify that the mapped name is valid before trying to use it + try { + Charset.forName(alias); + } catch (Exception ex) { + alias = null; // charset alias not valid, use original name + } + } + return alias == null ? charset : alias; + } + + /** + * Convert a java charset into its MIME charset name.

    + * + * Note that a future version of JDK (post 1.2) might provide + * this functionality, in which case, we may deprecate this + * method then. + * + * @param charset the JDK charset + * @return the MIME/IANA equivalent. If a mapping + * is not possible, the passed in charset itself + * is returned. + * @since JavaMail 1.1 + */ + public static String mimeCharset(String charset) { + if (java2mime == null || charset == null) + // no mapping table or charset param is null + return charset; + + String alias = java2mime.get(charset.toLowerCase(Locale.ENGLISH)); + return alias == null ? charset : alias; + } + + private static String defaultJavaCharset; + private static String defaultMIMECharset; + + /** + * Get the default charset corresponding to the system's current + * default locale. If the System property mail.mime.charset + * is set, a system charset corresponding to this MIME charset will be + * returned.

    + * + * @return the default charset of the system's default locale, + * as a Java charset. (NOT a MIME charset) + * @since JavaMail 1.1 + */ + public static String getDefaultJavaCharset() { + if (defaultJavaCharset == null) { + /* + * If mail.mime.charset is set, it controls the default + * Java charset as well. + */ + String mimecs = null; + try { + mimecs = System.getProperty("mail.mime.charset"); + } catch (SecurityException ex) { } // ignore it + if (mimecs != null && mimecs.length() > 0) { + defaultJavaCharset = javaCharset(mimecs); + return defaultJavaCharset; + } + + try { + defaultJavaCharset = System.getProperty("file.encoding", + "8859_1"); + } catch (SecurityException sex) { + + class NullInputStream extends InputStream { + @Override + public int read() { + return 0; + } + } + InputStreamReader reader = + new InputStreamReader(new NullInputStream()); + defaultJavaCharset = reader.getEncoding(); + if (defaultJavaCharset == null) + defaultJavaCharset = "8859_1"; + } + } + + return defaultJavaCharset; + } + + /* + * Get the default MIME charset for this locale. + */ + static String getDefaultMIMECharset() { + if (defaultMIMECharset == null) { + try { + defaultMIMECharset = System.getProperty("mail.mime.charset"); + } catch (SecurityException ex) { } // ignore it + } + if (defaultMIMECharset == null) + defaultMIMECharset = mimeCharset(getDefaultJavaCharset()); + return defaultMIMECharset; + } + + // Tables to map MIME charset names to Java names and vice versa. + // XXX - Should eventually use J2SE 1.4 java.nio.charset.Charset + private static Map mime2java; + private static Map java2mime; + + static { + java2mime = new HashMap<>(40); + mime2java = new HashMap<>(14); + + try { + // Use this class's classloader to load the mapping file + // XXX - we should use SecuritySupport, but it's in another package + InputStream is = + javax.mail.internet.MimeUtility.class.getResourceAsStream( + "/META-INF/javamail.charset.map"); + + if (is != null) { + try { + is = new LineInputStream(is); + + // Load the JDK-to-MIME charset mapping table + loadMappings((LineInputStream)is, java2mime); + + // Load the MIME-to-JDK charset mapping table + loadMappings((LineInputStream)is, mime2java); + } finally { + try { + is.close(); + } catch (Exception cex) { + // ignore + } + } + } + } catch (Exception ex) { } + + // If we didn't load the tables, e.g., because we didn't have + // permission, load them manually. The entries here should be + // the same as the default javamail.charset.map. + if (java2mime.isEmpty()) { + java2mime.put("8859_1", "ISO-8859-1"); + java2mime.put("iso8859_1", "ISO-8859-1"); + java2mime.put("iso8859-1", "ISO-8859-1"); + + java2mime.put("8859_2", "ISO-8859-2"); + java2mime.put("iso8859_2", "ISO-8859-2"); + java2mime.put("iso8859-2", "ISO-8859-2"); + + java2mime.put("8859_3", "ISO-8859-3"); + java2mime.put("iso8859_3", "ISO-8859-3"); + java2mime.put("iso8859-3", "ISO-8859-3"); + + java2mime.put("8859_4", "ISO-8859-4"); + java2mime.put("iso8859_4", "ISO-8859-4"); + java2mime.put("iso8859-4", "ISO-8859-4"); + + java2mime.put("8859_5", "ISO-8859-5"); + java2mime.put("iso8859_5", "ISO-8859-5"); + java2mime.put("iso8859-5", "ISO-8859-5"); + + java2mime.put("8859_6", "ISO-8859-6"); + java2mime.put("iso8859_6", "ISO-8859-6"); + java2mime.put("iso8859-6", "ISO-8859-6"); + + java2mime.put("8859_7", "ISO-8859-7"); + java2mime.put("iso8859_7", "ISO-8859-7"); + java2mime.put("iso8859-7", "ISO-8859-7"); + + java2mime.put("8859_8", "ISO-8859-8"); + java2mime.put("iso8859_8", "ISO-8859-8"); + java2mime.put("iso8859-8", "ISO-8859-8"); + + java2mime.put("8859_9", "ISO-8859-9"); + java2mime.put("iso8859_9", "ISO-8859-9"); + java2mime.put("iso8859-9", "ISO-8859-9"); + + java2mime.put("sjis", "Shift_JIS"); + java2mime.put("jis", "ISO-2022-JP"); + java2mime.put("iso2022jp", "ISO-2022-JP"); + java2mime.put("euc_jp", "euc-jp"); + java2mime.put("koi8_r", "koi8-r"); + java2mime.put("euc_cn", "euc-cn"); + java2mime.put("euc_tw", "euc-tw"); + java2mime.put("euc_kr", "euc-kr"); + } + if (mime2java.isEmpty()) { + mime2java.put("iso-2022-cn", "ISO2022CN"); + mime2java.put("iso-2022-kr", "ISO2022KR"); + mime2java.put("utf-8", "UTF8"); + mime2java.put("utf8", "UTF8"); + mime2java.put("ja_jp.iso2022-7", "ISO2022JP"); + mime2java.put("ja_jp.eucjp", "EUCJIS"); + mime2java.put("euc-kr", "KSC5601"); + mime2java.put("euckr", "KSC5601"); + mime2java.put("us-ascii", "ISO-8859-1"); + mime2java.put("x-us-ascii", "ISO-8859-1"); + mime2java.put("gb2312", "GB18030"); + mime2java.put("cp936", "GB18030"); + mime2java.put("ms936", "GB18030"); + mime2java.put("gbk", "GB18030"); + } + } + + private static void loadMappings(LineInputStream is, + Map table) { + String currLine; + + while (true) { + try { + currLine = is.readLine(); + } catch (IOException ioex) { + break; // error in reading, stop + } + + if (currLine == null) // end of file, stop + break; + if (currLine.startsWith("--") && currLine.endsWith("--")) + // end of this table + break; + + // ignore empty lines and comments + if (currLine.trim().length() == 0 || currLine.startsWith("#")) + continue; + + // A valid entry is of the form + // where, := SPACE | HT. Parse this + StringTokenizer tk = new StringTokenizer(currLine, " \t"); + try { + String key = tk.nextToken(); + String value = tk.nextToken(); + table.put(key.toLowerCase(Locale.ENGLISH), value); + } catch (NoSuchElementException nex) { } + } + } + + static final int ALL_ASCII = 1; + static final int MOSTLY_ASCII = 2; + static final int MOSTLY_NONASCII = 3; + + /** + * Check if the given string contains non US-ASCII characters. + * @param s string + * @return ALL_ASCII if all characters in the string + * belong to the US-ASCII charset. MOSTLY_ASCII + * if more than half of the available characters + * are US-ASCII characters. Else MOSTLY_NONASCII. + */ + static int checkAscii(String s) { + int ascii = 0, non_ascii = 0; + int l = s.length(); + + for (int i = 0; i < l; i++) { + if (nonascii((int)s.charAt(i))) // non-ascii + non_ascii++; + else + ascii++; + } + + if (non_ascii == 0) + return ALL_ASCII; + if (ascii > non_ascii) + return MOSTLY_ASCII; + + return MOSTLY_NONASCII; + } + + /** + * Check if the given byte array contains non US-ASCII characters. + * @param b byte array + * @return ALL_ASCII if all characters in the string + * belong to the US-ASCII charset. MOSTLY_ASCII + * if more than half of the available characters + * are US-ASCII characters. Else MOSTLY_NONASCII. + * + * XXX - this method is no longer used + */ + static int checkAscii(byte[] b) { + int ascii = 0, non_ascii = 0; + + for (int i=0; i < b.length; i++) { + // The '&' operator automatically causes b[i] to be promoted + // to an int, and we mask out the higher bytes in the int + // so that the resulting value is not a negative integer. + if (nonascii(b[i] & 0xff)) // non-ascii + non_ascii++; + else + ascii++; + } + + if (non_ascii == 0) + return ALL_ASCII; + if (ascii > non_ascii) + return MOSTLY_ASCII; + + return MOSTLY_NONASCII; + } + + /** + * Check if the given input stream contains non US-ASCII characters. + * Upto max bytes are checked. If max is + * set to ALL, then all the bytes available in this + * input stream are checked. If breakOnNonAscii is true + * the check terminates when the first non-US-ASCII character is + * found and MOSTLY_NONASCII is returned. Else, the check continues + * till max bytes or till the end of stream. + * + * @param is the input stream + * @param max maximum bytes to check for. The special value + * ALL indicates that all the bytes in this input + * stream must be checked. + * @param breakOnNonAscii if true, then terminate the + * the check when the first non-US-ASCII character + * is found. + * @return ALL_ASCII if all characters in the string + * belong to the US-ASCII charset. MOSTLY_ASCII + * if more than half of the available characters + * are US-ASCII characters. Else MOSTLY_NONASCII. + */ + static int checkAscii(InputStream is, int max, boolean breakOnNonAscii) { + int ascii = 0, non_ascii = 0; + int len; + int block = 4096; + int linelen = 0; + boolean longLine = false, badEOL = false; + boolean checkEOL = encodeEolStrict && breakOnNonAscii; + byte buf[] = null; + if (max != 0) { + block = (max == ALL) ? 4096 : Math.min(max, 4096); + buf = new byte[block]; + } + while (max != 0) { + try { + if ((len = is.read(buf, 0, block)) == -1) + break; + int lastb = 0; + for (int i = 0; i < len; i++) { + // The '&' operator automatically causes b[i] to + // be promoted to an int, and we mask out the higher + // bytes in the int so that the resulting value is + // not a negative integer. + int b = buf[i] & 0xff; + if (checkEOL && + ((lastb == '\r' && b != '\n') || + (lastb != '\r' && b == '\n'))) + badEOL = true; + if (b == '\r' || b == '\n') + linelen = 0; + else { + linelen++; + if (linelen > 998) // 1000 - CRLF + longLine = true; + } + if (nonascii(b)) { // non-ascii + if (breakOnNonAscii) // we are done + return MOSTLY_NONASCII; + else + non_ascii++; + } else + ascii++; + lastb = b; + } + } catch (IOException ioex) { + break; + } + if (max != ALL) + max -= len; + } + + if (max == 0 && breakOnNonAscii) + // We have been told to break on the first non-ascii character. + // We haven't got any non-ascii character yet, but then we + // have not checked all of the available bytes either. So we + // cannot say for sure that this input stream is ALL_ASCII, + // and hence we must play safe and return MOSTLY_NONASCII + + return MOSTLY_NONASCII; + + if (non_ascii == 0) { // no non-us-ascii characters so far + // If we're looking at non-text data, and we saw CR without LF + // or vice versa, consider this mostly non-ASCII so that it + // will be base64 encoded (since the quoted-printable encoder + // doesn't encode this case properly). + if (badEOL) + return MOSTLY_NONASCII; + // if we've seen a long line, we degrade to mostly ascii + else if (longLine) + return MOSTLY_ASCII; + else + return ALL_ASCII; + } + if (ascii > non_ascii) // mostly ascii + return MOSTLY_ASCII; + return MOSTLY_NONASCII; + } + + static final boolean nonascii(int b) { + return b >= 0177 || (b < 040 && b != '\r' && b != '\n' && b != '\t'); + } +} + +/** + * An OutputStream that determines whether the data written to + * it is all ASCII, mostly ASCII, or mostly non-ASCII. + */ +class AsciiOutputStream extends OutputStream { + private boolean breakOnNonAscii; + private int ascii = 0, non_ascii = 0; + private int linelen = 0; + private boolean longLine = false; + private boolean badEOL = false; + private boolean checkEOL = false; + private int lastb = 0; + private int ret = 0; + + public AsciiOutputStream(boolean breakOnNonAscii, boolean encodeEolStrict) { + this.breakOnNonAscii = breakOnNonAscii; + checkEOL = encodeEolStrict && breakOnNonAscii; + } + + @Override + public void write(int b) throws IOException { + check(b); + } + + @Override + public void write(byte b[]) throws IOException { + write(b, 0, b.length); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + len += off; + for (int i = off; i < len ; i++) + check(b[i]); + } + + private final void check(int b) throws IOException { + b &= 0xff; + if (checkEOL && + ((lastb == '\r' && b != '\n') || (lastb != '\r' && b == '\n'))) + badEOL = true; + if (b == '\r' || b == '\n') + linelen = 0; + else { + linelen++; + if (linelen > 998) // 1000 - CRLF + longLine = true; + } + if (MimeUtility.nonascii(b)) { // non-ascii + non_ascii++; + if (breakOnNonAscii) { // we are done + ret = MimeUtility.MOSTLY_NONASCII; + throw new EOFException(); + } + } else + ascii++; + lastb = b; + } + + /** + * Return ASCII-ness of data stream. + */ + public int getAscii() { + if (ret != 0) + return ret; + // If we're looking at non-text data, and we saw CR without LF + // or vice versa, consider this mostly non-ASCII so that it + // will be base64 encoded (since the quoted-printable encoder + // doesn't encode this case properly). + if (badEOL) + return MimeUtility.MOSTLY_NONASCII; + else if (non_ascii == 0) { // no non-us-ascii characters so far + // if we've seen a long line, we degrade to mostly ascii + if (longLine) + return MimeUtility.MOSTLY_ASCII; + else + return MimeUtility.ALL_ASCII; + } + if (ascii > non_ascii) // mostly ascii + return MimeUtility.MOSTLY_ASCII; + return MimeUtility.MOSTLY_NONASCII; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/NewsAddress.java b/fine-third-default/fine-mail/src/javax/mail/internet/NewsAddress.java new file mode 100644 index 000000000..3af8843f6 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/NewsAddress.java @@ -0,0 +1,226 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; +import java.util.Locale; +import javax.mail.*; + +/** + * This class models an RFC1036 newsgroup address. + * + * @author Bill Shannon + * @author John Mani + */ + +public class NewsAddress extends Address { + + protected String newsgroup; + protected String host; // may be null + + private static final long serialVersionUID = -4203797299824684143L; + + /** + * Default constructor. + */ + public NewsAddress() { } + + /** + * Construct a NewsAddress with the given newsgroup. + * + * @param newsgroup the newsgroup + */ + public NewsAddress(String newsgroup) { + this(newsgroup, null); + } + + /** + * Construct a NewsAddress with the given newsgroup and host. + * + * @param newsgroup the newsgroup + * @param host the host + */ + public NewsAddress(String newsgroup, String host) { + // XXX - this method should throw an exception so we can report + // illegal addresses, but for now just remove whitespace + this.newsgroup = newsgroup.replaceAll("\\s+", ""); + this.host = host; + } + + /** + * Return the type of this address. The type of a NewsAddress + * is "news". + */ + @Override + public String getType() { + return "news"; + } + + /** + * Set the newsgroup. + * + * @param newsgroup the newsgroup + */ + public void setNewsgroup(String newsgroup) { + this.newsgroup = newsgroup; + } + + /** + * Get the newsgroup. + * + * @return newsgroup + */ + public String getNewsgroup() { + return newsgroup; + } + + /** + * Set the host. + * + * @param host the host + */ + public void setHost(String host) { + this.host = host; + } + + /** + * Get the host. + * + * @return host + */ + public String getHost() { + return host; + } + + /** + * Convert this address into a RFC 1036 address. + * + * @return newsgroup + */ + @Override + public String toString() { + return newsgroup; + } + + /** + * The equality operator. + */ + @Override + public boolean equals(Object a) { + if (!(a instanceof NewsAddress)) + return false; + + NewsAddress s = (NewsAddress)a; + return ((newsgroup == null && s.newsgroup == null) || + (newsgroup != null && newsgroup.equals(s.newsgroup))) && + ((host == null && s.host == null) || + (host != null && s.host != null && host.equalsIgnoreCase(s.host))); + } + + /** + * Compute a hash code for the address. + */ + @Override + public int hashCode() { + int hash = 0; + if (newsgroup != null) + hash += newsgroup.hashCode(); + if (host != null) + hash += host.toLowerCase(Locale.ENGLISH).hashCode(); + return hash; + } + + /** + * Convert the given array of NewsAddress objects into + * a comma separated sequence of address strings. The + * resulting string contains only US-ASCII characters, and + * hence is mail-safe. + * + * @param addresses array of NewsAddress objects + * @exception ClassCastException if any address object in the + * given array is not a NewsAddress objects. Note + * that this is a RuntimeException. + * @return comma separated address strings + */ + public static String toString(Address[] addresses) { + if (addresses == null || addresses.length == 0) + return null; + + StringBuilder s = + new StringBuilder(((NewsAddress)addresses[0]).toString()); + int used = s.length(); + for (int i = 1; i < addresses.length; i++) { + s.append(","); + used++; + String ng = ((NewsAddress)addresses[i]).toString(); + if (used + ng.length() > 76) { + s.append("\r\n\t"); + used = 8; + } + s.append(ng); + used += ng.length(); + } + + return s.toString(); + } + + /** + * Parse the given comma separated sequence of newsgroups into + * NewsAddress objects. + * + * @param newsgroups comma separated newsgroup string + * @return array of NewsAddress objects + * @exception AddressException if the parse failed + */ + public static NewsAddress[] parse(String newsgroups) + throws AddressException { + // XXX - verify format of newsgroup name? + StringTokenizer st = new StringTokenizer(newsgroups, ","); + List nglist = new ArrayList<>(); + while (st.hasMoreTokens()) { + String ng = st.nextToken(); + nglist.add(new NewsAddress(ng)); + } + return nglist.toArray(new NewsAddress[nglist.size()]); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/ParameterList.java b/fine-third-default/fine-mail/src/javax/mail/internet/ParameterList.java new file mode 100644 index 000000000..c1f07c726 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/ParameterList.java @@ -0,0 +1,917 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import java.util.*; +import java.io.*; +import com.sun.mail.util.PropUtil; +import com.sun.mail.util.ASCIIUtility; + +/** + * This class holds MIME parameters (attribute-value pairs). + * The mail.mime.encodeparameters and + * mail.mime.decodeparameters System properties + * control whether encoded parameters, as specified by + * RFC 2231, + * are supported. By default, such encoded parameters are + * supported.

    + * + * Also, in the current implementation, setting the System property + * mail.mime.decodeparameters.strict to "true" + * will cause a ParseException to be thrown for errors + * detected while decoding encoded parameters. By default, if any + * decoding errors occur, the original (undecoded) string is used.

    + * + * The current implementation supports the System property + * mail.mime.parameters.strict, which if set to false + * when parsing a parameter list allows parameter values + * to contain whitespace and other special characters without + * being quoted; the parameter value ends at the next semicolon. + * If set to true (the default), parameter values are required to conform + * to the MIME specification and must be quoted if they contain whitespace + * or special characters. + * + * @author John Mani + * @author Bill Shannon + */ + +public class ParameterList { + + /** + * The map of name, value pairs. + * The value object is either a String, for unencoded + * values, or a Value object, for encoded values, + * or a MultiValue object, for multi-segment parameters, + * or a LiteralValue object for strings that should not be encoded. + * + * We use a LinkedHashMap so that parameters are (as much as + * possible) kept in the original order. Note however that + * multi-segment parameters (see below) will appear in the + * position of the first seen segment and orphan segments + * will all move to the end. + */ + // keep parameters in order + private Map list = new LinkedHashMap<>(); + + /** + * A set of names for multi-segment parameters that we + * haven't processed yet. Normally such names are accumulated + * during the inital parse and processed at the end of the parse, + * but such names can also be set via the set method when the + * IMAP provider accumulates pre-parsed pieces of a parameter list. + * (A special call to the set method tells us when the IMAP provider + * is done setting parameters.) + * + * A multi-segment parameter is defined by RFC 2231. For example, + * "title*0=part1; title*1=part2", which represents a parameter + * named "title" with value "part1part2". + * + * Note also that each segment of the value might or might not be + * encoded, indicated by a trailing "*" on the parameter name. + * If any segment is encoded, the first segment must be encoded. + * Only the first segment contains the charset and language + * information needed to decode any encoded segments. + * + * RFC 2231 introduces many possible failure modes, which we try + * to handle as gracefully as possible. Generally, a failure to + * decode a parameter value causes the non-decoded parameter value + * to be used instead. Missing segments cause all later segments + * to be appear as independent parameters with names that include + * the segment number. For example, "title*0=part1; title*1=part2; + * title*3=part4" appears as two parameters named "title" and "title*3". + */ + private Set multisegmentNames; + + /** + * A map containing the segments for all not-yet-processed + * multi-segment parameters. The map is indexed by "name*seg". + * The value object is either a String or a Value object. + * The Value object is not decoded during the initial parse + * because the segments may appear in any order and until the + * first segment appears we don't know what charset to use to + * decode the encoded segments. The segments are hex decoded + * in order, combined into a single byte array, and converted + * to a String using the specified charset in the + * combineMultisegmentNames method. + */ + private Map slist; + + /** + * MWB 3BView: The name of the last parameter added to the map. + * Used for the AppleMail hack. + */ + private String lastName = null; + + private static final boolean encodeParameters = + PropUtil.getBooleanSystemProperty("mail.mime.encodeparameters", true); + private static final boolean decodeParameters = + PropUtil.getBooleanSystemProperty("mail.mime.decodeparameters", true); + private static final boolean decodeParametersStrict = + PropUtil.getBooleanSystemProperty( + "mail.mime.decodeparameters.strict", false); + private static final boolean applehack = + PropUtil.getBooleanSystemProperty("mail.mime.applefilenames", false); + private static final boolean windowshack = + PropUtil.getBooleanSystemProperty("mail.mime.windowsfilenames", false); + private static final boolean parametersStrict = + PropUtil.getBooleanSystemProperty("mail.mime.parameters.strict", true); + private static final boolean splitLongParameters = + PropUtil.getBooleanSystemProperty( + "mail.mime.splitlongparameters", true); + + + /** + * A struct to hold an encoded value. + * A parsed encoded value is stored as both the + * decoded value and the original encoded value + * (so that toString will produce the same result). + * An encoded value that is set explicitly is stored + * as the original value and the encoded value, to + * ensure that get will return the same value that + * was set. + */ + private static class Value { + String value; + String charset; + String encodedValue; + } + + /** + * A struct to hold a literal value that shouldn't be further encoded. + */ + private static class LiteralValue { + String value; + } + + /** + * A struct for a multi-segment parameter. Each entry in the + * List is either a String or a Value object. When all the + * segments are present and combined in the combineMultisegmentNames + * method, the value field contains the combined and decoded value. + * Until then the value field contains an empty string as a placeholder. + */ + private static class MultiValue extends ArrayList { + // keep lint happy + private static final long serialVersionUID = 699561094618751023L; + + String value; + } + + /** + * Map the LinkedHashMap's keySet iterator to an Enumeration. + */ + private static class ParamEnum implements Enumeration { + private Iterator it; + + ParamEnum(Iterator it) { + this.it = it; + } + + @Override + public boolean hasMoreElements() { + return it.hasNext(); + } + + @Override + public String nextElement() { + return it.next(); + } + } + + /** + * No-arg Constructor. + */ + public ParameterList() { + // initialize other collections only if they'll be needed + if (decodeParameters) { + multisegmentNames = new HashSet<>(); + slist = new HashMap<>(); + } + } + + /** + * Constructor that takes a parameter-list string. The String + * is parsed and the parameters are collected and stored internally. + * A ParseException is thrown if the parse fails. + * Note that an empty parameter-list string is valid and will be + * parsed into an empty ParameterList. + * + * @param s the parameter-list string. + * @exception ParseException if the parse fails. + */ + public ParameterList(String s) throws ParseException { + this(); + + HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME); + for (;;) { + HeaderTokenizer.Token tk = h.next(); + int type = tk.getType(); + String name, value; + + if (type == HeaderTokenizer.Token.EOF) // done + break; + + if ((char)type == ';') { + // expect parameter name + tk = h.next(); + // tolerate trailing semicolon, even though it violates the spec + if (tk.getType() == HeaderTokenizer.Token.EOF) + break; + // parameter name must be a MIME Atom + if (tk.getType() != HeaderTokenizer.Token.ATOM) + throw new ParseException("In parameter list <" + s + ">" + + ", expected parameter name, " + + "got \"" + tk.getValue() + "\""); + name = tk.getValue().toLowerCase(Locale.ENGLISH); + + // expect '=' + tk = h.next(); + if ((char)tk.getType() != '=') + throw new ParseException("In parameter list <" + s + ">" + + ", expected '=', " + + "got \"" + tk.getValue() + "\""); + + // expect parameter value + if (windowshack && + (name.equals("name") || name.equals("filename"))) + tk = h.next(';', true); + else if (parametersStrict) + tk = h.next(); + else + tk = h.next(';'); + type = tk.getType(); + // parameter value must be a MIME Atom or Quoted String + if (type != HeaderTokenizer.Token.ATOM && + type != HeaderTokenizer.Token.QUOTEDSTRING) + throw new ParseException("In parameter list <" + s + ">" + + ", expected parameter value, " + + "got \"" + tk.getValue() + "\""); + + value = tk.getValue(); + lastName = name; + if (decodeParameters) + putEncodedName(name, value); + else + list.put(name, value); + } else { + // MWB 3BView new code to add in filenames generated by + // AppleMail. + // Note - one space is assumed between name elements. + // This may not be correct but it shouldn't matter too much. + // Note: AppleMail encodes filenames with non-ascii characters + // correctly, so we don't need to worry about the name* subkeys. + if (type == HeaderTokenizer.Token.ATOM && lastName != null && + ((applehack && + (lastName.equals("name") || + lastName.equals("filename"))) || + !parametersStrict) + ) { + // Add value to previous value + String lastValue = (String)list.get(lastName); + value = lastValue + " " + tk.getValue(); + list.put(lastName, value); + } else { + throw new ParseException("In parameter list <" + s + ">" + + ", expected ';', got \"" + + tk.getValue() + "\""); + } + } + } + + if (decodeParameters) { + /* + * After parsing all the parameters, combine all the + * multi-segment parameter values together. + */ + combineMultisegmentNames(false); + } + } + + /** + * Normal users of this class will use simple parameter names. + * In some cases, for example, when processing IMAP protocol + * messages, individual segments of a multi-segment name + * (specified by RFC 2231) will be encountered and passed to + * the {@link #set} method. After all these segments are added + * to this ParameterList, they need to be combined to represent + * the logical parameter name and value. This method will combine + * all segments of multi-segment names.

    + * + * Normal users should never need to call this method. + * + * @since JavaMail 1.5 + */ + public void combineSegments() { + /* + * If we've accumulated any multi-segment names from calls to + * the set method from (e.g.) the IMAP provider, combine the pieces. + * Ignore any parse errors (e.g., from decoding the values) + * because it's too late to report them. + */ + if (decodeParameters && multisegmentNames.size() > 0) { + try { + combineMultisegmentNames(true); + } catch (ParseException pex) { + // too late to do anything about it + } + } + } + + /** + * If the name is an encoded or multi-segment name (or both) + * handle it appropriately, storing the appropriate String + * or Value object. Multi-segment names are stored in the + * main parameter list as an emtpy string as a placeholder, + * replaced later in combineMultisegmentNames with a MultiValue + * object. This causes all pieces of the multi-segment parameter + * to appear in the position of the first seen segment of the + * parameter. + */ + private void putEncodedName(String name, String value) + throws ParseException { + int star = name.indexOf('*'); + if (star < 0) { + // single parameter, unencoded value + list.put(name, value); + } else if (star == name.length() - 1) { + // single parameter, encoded value + name = name.substring(0, star); + Value v = extractCharset(value); + try { + v.value = decodeBytes(v.value, v.charset); + } catch (UnsupportedEncodingException ex) { + if (decodeParametersStrict) + throw new ParseException(ex.toString()); + } + list.put(name, v); + } else { + // multiple segments + String rname = name.substring(0, star); + multisegmentNames.add(rname); + list.put(rname, ""); + + Object v; + if (name.endsWith("*")) { + // encoded value + if (name.endsWith("*0*")) { // first segment + v = extractCharset(value); + } else { + v = new Value(); + ((Value)v).encodedValue = value; + ((Value)v).value = value; // default; decoded later + } + name = name.substring(0, name.length() - 1); + } else { + // unencoded value + v = value; + } + slist.put(name, v); + } + } + + /** + * Iterate through the saved set of names of multi-segment parameters, + * for each parameter find all segments stored in the slist map, + * decode each segment as needed, combine the segments together into + * a single decoded value, and save all segments in a MultiValue object + * in the main list indexed by the parameter name. + */ + private void combineMultisegmentNames(boolean keepConsistentOnFailure) + throws ParseException { + boolean success = false; + try { + Iterator it = multisegmentNames.iterator(); + while (it.hasNext()) { + String name = it.next(); + MultiValue mv = new MultiValue(); + /* + * Now find all the segments for this name and + * decode each segment as needed. + */ + String charset = null; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + int segment; + for (segment = 0; ; segment++) { + String sname = name + "*" + segment; + Object v = slist.get(sname); + if (v == null) // out of segments + break; + mv.add(v); + try { + if (v instanceof Value) { + Value vv = (Value)v; + if (segment == 0) { + // the first segment specifies the charset + // for all other encoded segments + charset = vv.charset; + } else { + if (charset == null) { + // should never happen + multisegmentNames.remove(name); + break; + } + } + decodeBytes(vv.value, bos); + } else { + bos.write(ASCIIUtility.getBytes((String)v)); + } + } catch (IOException ex) { + // XXX - should never happen + } + slist.remove(sname); + } + if (segment == 0) { + // didn't find any segments at all + list.remove(name); + } else { + try { + if (charset != null) + charset = MimeUtility.javaCharset(charset); + if (charset == null || charset.length() == 0) + charset = MimeUtility.getDefaultJavaCharset(); + if (charset != null) + mv.value = bos.toString(charset); + else + mv.value = bos.toString(); + } catch (UnsupportedEncodingException uex) { + if (decodeParametersStrict) + throw new ParseException(uex.toString()); + // convert as if iso-8859-1 + try { + mv.value = bos.toString("iso-8859-1"); + } catch (UnsupportedEncodingException ex) { + // should never happen + } + } + list.put(name, mv); + } + } + success = true; + } finally { + /* + * If we get here because of an exception that's going to + * be thrown (success == false) from the constructor + * (keepConsistentOnFailure == false), this is all wasted effort. + */ + if (keepConsistentOnFailure || success) { + // we should never end up with anything in slist, + // but if we do, add it all to list + if (slist.size() > 0) { + // first, decode any values that we'll add to the list + Iterator sit = slist.values().iterator(); + while (sit.hasNext()) { + Object v = sit.next(); + if (v instanceof Value) { + Value vv = (Value)v; + try { + vv.value = + decodeBytes(vv.value, vv.charset); + } catch (UnsupportedEncodingException ex) { + if (decodeParametersStrict) + throw new ParseException(ex.toString()); + } + } + } + list.putAll(slist); + } + + // clear out the set of names and segments + multisegmentNames.clear(); + slist.clear(); + } + } + } + + /** + * Return the number of parameters in this list. + * + * @return number of parameters. + */ + public int size() { + return list.size(); + } + + /** + * Returns the value of the specified parameter. Note that + * parameter names are case-insensitive. + * + * @param name parameter name. + * @return Value of the parameter. Returns + * null if the parameter is not + * present. + */ + public String get(String name) { + String value; + Object v = list.get(name.trim().toLowerCase(Locale.ENGLISH)); + if (v instanceof MultiValue) + value = ((MultiValue)v).value; + else if (v instanceof LiteralValue) + value = ((LiteralValue)v).value; + else if (v instanceof Value) + value = ((Value)v).value; + else + value = (String)v; + return value; + } + + /** + * Set a parameter. If this parameter already exists, it is + * replaced by this new value. + * + * @param name name of the parameter. + * @param value value of the parameter. + */ + public void set(String name, String value) { + name = name.trim().toLowerCase(Locale.ENGLISH); + if (decodeParameters) { + try { + putEncodedName(name, value); + } catch (ParseException pex) { + // ignore it + list.put(name, value); + } + } else + list.put(name, value); + } + + /** + * Set a parameter. If this parameter already exists, it is + * replaced by this new value. If the + * mail.mime.encodeparameters System property + * is true, and the parameter value is non-ASCII, it will be + * encoded with the specified charset, as specified by RFC 2231. + * + * @param name name of the parameter. + * @param value value of the parameter. + * @param charset charset of the parameter value. + * @since JavaMail 1.4 + */ + public void set(String name, String value, String charset) { + if (encodeParameters) { + Value ev = encodeValue(value, charset); + // was it actually encoded? + if (ev != null) + list.put(name.trim().toLowerCase(Locale.ENGLISH), ev); + else + set(name, value); + } else + set(name, value); + } + + /** + * Package-private method to set a literal value that won't be + * further encoded. Used to set the filename parameter when + * "mail.mime.encodefilename" is true. + * + * @param name name of the parameter. + * @param value value of the parameter. + */ + void setLiteral(String name, String value) { + LiteralValue lv = new LiteralValue(); + lv.value = value; + list.put(name, lv); + } + + /** + * Removes the specified parameter from this ParameterList. + * This method does nothing if the parameter is not present. + * + * @param name name of the parameter. + */ + public void remove(String name) { + list.remove(name.trim().toLowerCase(Locale.ENGLISH)); + } + + /** + * Return an enumeration of the names of all parameters in this + * list. + * + * @return Enumeration of all parameter names in this list. + */ + public Enumeration getNames() { + return new ParamEnum(list.keySet().iterator()); + } + + /** + * Convert this ParameterList into a MIME String. If this is + * an empty list, an empty string is returned. + * + * @return String + */ + @Override + public String toString() { + return toString(0); + } + + /** + * Convert this ParameterList into a MIME String. If this is + * an empty list, an empty string is returned. + * + * The 'used' parameter specifies the number of character positions + * already taken up in the field into which the resulting parameter + * list is to be inserted. It's used to determine where to fold the + * resulting parameter list. + * + * @param used number of character positions already used, in + * the field into which the parameter list is to + * be inserted. + * @return String + */ + public String toString(int used) { + ToStringBuffer sb = new ToStringBuffer(used); + Iterator> e = list.entrySet().iterator(); + + while (e.hasNext()) { + Map.Entry ent = e.next(); + String name = ent.getKey(); + String value; + Object v = ent.getValue(); + if (v instanceof MultiValue) { + MultiValue vv = (MultiValue)v; + name += "*"; + for (int i = 0; i < vv.size(); i++) { + Object va = vv.get(i); + String ns; + if (va instanceof Value) { + ns = name + i + "*"; + value = ((Value)va).encodedValue; + } else { + ns = name + i; + value = (String)va; + } + sb.addNV(ns, quote(value)); + } + } else if (v instanceof LiteralValue) { + value = ((LiteralValue)v).value; + sb.addNV(name, quote(value)); + } else if (v instanceof Value) { + /* + * XXX - We could split the encoded value into multiple + * segments if it's too long, but that's more difficult. + */ + name += "*"; + value = ((Value)v).encodedValue; + sb.addNV(name, quote(value)); + } else { + value = (String)v; + /* + * If this value is "long", split it into a multi-segment + * parameter. Only do this if we've enabled RFC2231 style + * encoded parameters. + * + * Note that we check the length before quoting the value. + * Quoting might make the string longer, although typically + * not much, so we allow a little slop in the calculation. + * In the worst case, a 60 character string will turn into + * 122 characters when quoted, which is long but not + * outrageous. + */ + if (value.length() > 60 && + splitLongParameters && encodeParameters) { + int seg = 0; + name += "*"; + while (value.length() > 60) { + sb.addNV(name + seg, quote(value.substring(0, 60))); + value = value.substring(60); + seg++; + } + if (value.length() > 0) + sb.addNV(name + seg, quote(value)); + } else { + sb.addNV(name, quote(value)); + } + } + } + return sb.toString(); + } + + /** + * A special wrapper for a StringBuffer that keeps track of the + * number of characters used in a line, wrapping to a new line + * as necessary; for use by the toString method. + */ + private static class ToStringBuffer { + private int used; // keep track of how much used on current line + private StringBuilder sb = new StringBuilder(); + + public ToStringBuffer(int used) { + this.used = used; + } + + public void addNV(String name, String value) { + sb.append("; "); + used += 2; + int len = name.length() + value.length() + 1; + if (used + len > 76) { // overflows ... + sb.append("\r\n\t"); // .. start new continuation line + used = 8; // account for the starting char + } + sb.append(name).append('='); + used += name.length() + 1; + if (used + value.length() > 76) { // still overflows ... + // have to fold value + String s = MimeUtility.fold(used, value); + sb.append(s); + int lastlf = s.lastIndexOf('\n'); + if (lastlf >= 0) // always true + used += s.length() - lastlf - 1; + else + used += s.length(); + } else { + sb.append(value); + used += value.length(); + } + } + + @Override + public String toString() { + return sb.toString(); + } + } + + // Quote a parameter value token if required. + private static String quote(String value) { + return MimeUtility.quote(value, HeaderTokenizer.MIME); + } + + private static final char hex[] = { + '0','1', '2', '3', '4', '5', '6', '7', + '8','9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + /** + * Encode a parameter value, if necessary. + * If the value is encoded, a Value object is returned. + * Otherwise, null is returned. + * XXX - Could return a MultiValue object if parameter value is too long. + */ + private static Value encodeValue(String value, String charset) { + if (MimeUtility.checkAscii(value) == MimeUtility.ALL_ASCII) + return null; // no need to encode it + + byte[] b; // charset encoded bytes from the string + try { + b = value.getBytes(MimeUtility.javaCharset(charset)); + } catch (UnsupportedEncodingException ex) { + return null; + } + StringBuffer sb = new StringBuffer(b.length + charset.length() + 2); + sb.append(charset).append("''"); + for (int i = 0; i < b.length; i++) { + char c = (char)(b[i] & 0xff); + // do we need to encode this character? + if (c <= ' ' || c >= 0x7f || c == '*' || c == '\'' || c == '%' || + HeaderTokenizer.MIME.indexOf(c) >= 0) { + sb.append('%').append(hex[c>>4]).append(hex[c&0xf]); + } else + sb.append(c); + } + Value v = new Value(); + v.charset = charset; + v.value = value; + v.encodedValue = sb.toString(); + return v; + } + + /** + * Extract charset and encoded value. + * Value will be decoded later. + */ + private static Value extractCharset(String value) throws ParseException { + Value v = new Value(); + v.value = v.encodedValue = value; + try { + int i = value.indexOf('\''); + if (i < 0) { + if (decodeParametersStrict) + throw new ParseException( + "Missing charset in encoded value: " + value); + return v; // not encoded correctly? return as is. + } + String charset = value.substring(0, i); + int li = value.indexOf('\'', i + 1); + if (li < 0) { + if (decodeParametersStrict) + throw new ParseException( + "Missing language in encoded value: " + value); + return v; // not encoded correctly? return as is. + } + // String lang = value.substring(i + 1, li); + v.value = value.substring(li + 1); + v.charset = charset; + } catch (NumberFormatException nex) { + if (decodeParametersStrict) + throw new ParseException(nex.toString()); + } catch (StringIndexOutOfBoundsException ex) { + if (decodeParametersStrict) + throw new ParseException(ex.toString()); + } + return v; + } + + /** + * Decode the encoded bytes in value using the specified charset. + */ + private static String decodeBytes(String value, String charset) + throws ParseException, UnsupportedEncodingException { + /* + * Decode the ASCII characters in value + * into an array of bytes, and then convert + * the bytes to a String using the specified + * charset. We'll never need more bytes than + * encoded characters, so use that to size the + * array. + */ + byte[] b = new byte[value.length()]; + int i, bi; + for (i = 0, bi = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '%') { + try { + String hex = value.substring(i + 1, i + 3); + c = (char)Integer.parseInt(hex, 16); + i += 2; + } catch (NumberFormatException ex) { + if (decodeParametersStrict) + throw new ParseException(ex.toString()); + } catch (StringIndexOutOfBoundsException ex) { + if (decodeParametersStrict) + throw new ParseException(ex.toString()); + } + } + b[bi++] = (byte)c; + } + if (charset != null) + charset = MimeUtility.javaCharset(charset); + if (charset == null || charset.length() == 0) + charset = MimeUtility.getDefaultJavaCharset(); + return new String(b, 0, bi, charset); + } + + /** + * Decode the encoded bytes in value and write them to the OutputStream. + */ + private static void decodeBytes(String value, OutputStream os) + throws ParseException, IOException { + /* + * Decode the ASCII characters in value + * and write them to the stream. + */ + int i; + for (i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '%') { + try { + String hex = value.substring(i + 1, i + 3); + c = (char)Integer.parseInt(hex, 16); + i += 2; + } catch (NumberFormatException ex) { + if (decodeParametersStrict) + throw new ParseException(ex.toString()); + } catch (StringIndexOutOfBoundsException ex) { + if (decodeParametersStrict) + throw new ParseException(ex.toString()); + } + } + os.write((byte)c); + } + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/ParseException.java b/fine-third-default/fine-mail/src/javax/mail/internet/ParseException.java new file mode 100644 index 000000000..d3d7bb6b5 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/ParseException.java @@ -0,0 +1,70 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import javax.mail.MessagingException; + +/** + * The exception thrown due to an error in parsing RFC822 + * or MIME headers, including multipart bodies. + * + * @author John Mani + */ + +public class ParseException extends MessagingException { + + private static final long serialVersionUID = 7649991205183658089L; + + /** + * Constructs a ParseException with no detail message. + */ + public ParseException() { + super(); + } + + /** + * Constructs a ParseException with the specified detail message. + * @param s the detail message + */ + public ParseException(String s) { + super(s); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/PreencodedMimeBodyPart.java b/fine-third-default/fine-mail/src/javax/mail/internet/PreencodedMimeBodyPart.java new file mode 100644 index 000000000..2ee6a6e19 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/PreencodedMimeBodyPart.java @@ -0,0 +1,129 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import java.io.*; +import java.util.Enumeration; +import javax.mail.*; + +import com.sun.mail.util.LineOutputStream; + +/** + * A MimeBodyPart that handles data that has already been encoded. + * This class is useful when constructing a message and attaching + * data that has already been encoded (for example, using base64 + * encoding). The data may have been encoded by the application, + * or may have been stored in a file or database in encoded form. + * The encoding is supplied when this object is created. The data + * is attached to this object in the usual fashion, by using the + * setText, setContent, or + * setDataHandler methods. + * + * @since JavaMail 1.4 + */ + +public class PreencodedMimeBodyPart extends MimeBodyPart { + private String encoding; + + /** + * Create a PreencodedMimeBodyPart that assumes the data is + * encoded using the specified encoding. The encoding must + * be a MIME supported Content-Transfer-Encoding. + * + * @param encoding the Content-Transfer-Encoding + */ + public PreencodedMimeBodyPart(String encoding) { + this.encoding = encoding; + } + + /** + * Returns the content transfer encoding specified when + * this object was created. + */ + @Override + public String getEncoding() throws MessagingException { + return encoding; + } + + /** + * Output the body part as an RFC 822 format stream. + * + * @exception IOException if an error occurs writing to the + * stream or if an error is generated + * by the javax.activation layer. + * @exception MessagingException for other failures + * @see javax.activation.DataHandler#writeTo + */ + @Override + public void writeTo(OutputStream os) + throws IOException, MessagingException { + + // see if we already have a LOS + LineOutputStream los = null; + if (os instanceof LineOutputStream) { + los = (LineOutputStream) os; + } else { + los = new LineOutputStream(os); + } + + // First, write out the header + Enumeration hdrLines = getAllHeaderLines(); + while (hdrLines.hasMoreElements()) + los.writeln(hdrLines.nextElement()); + + // The CRLF separator between header and content + los.writeln(); + + // Finally, the content, already encoded. + getDataHandler().writeTo(os); + os.flush(); + } + + /** + * Force the Content-Transfer-Encoding header to use + * the encoding that was specified when this object was created. + */ + @Override + protected void updateHeaders() throws MessagingException { + super.updateHeaders(); + MimeBodyPart.setEncoding(this, encoding); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/SharedInputStream.java b/fine-third-default/fine-mail/src/javax/mail/internet/SharedInputStream.java new file mode 100644 index 000000000..115c77b90 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/SharedInputStream.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import java.io.*; + +/** + * An InputStream that is backed by data that can be shared by multiple + * readers may implement this interface. This allows users of such an + * InputStream to determine the current position in the InputStream, and + * to create new InputStreams representing a subset of the data in the + * original InputStream. The new InputStream will access the same + * underlying data as the original, without copying the data.

    + * + * Note that implementations of this interface must ensure that the + * close method does not close any underlying stream + * that might be shared by multiple instances of SharedInputStream + * until all shared instances have been closed. + * + * @author Bill Shannon + * @since JavaMail 1.2 + */ + +public interface SharedInputStream { + /** + * Return the current position in the InputStream, as an + * offset from the beginning of the InputStream. + * + * @return the current position + */ + public long getPosition(); + + /** + * Return a new InputStream representing a subset of the data + * from this InputStream, starting at start (inclusive) + * up to end (exclusive). start must be + * non-negative. If end is -1, the new stream ends + * at the same place as this stream. The returned InputStream + * will also implement the SharedInputStream interface. + * + * @param start the starting position + * @param end the ending position + 1 + * @return the new stream + */ + public InputStream newStream(long start, long end); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/UniqueValue.java b/fine-third-default/fine-mail/src/javax/mail/internet/UniqueValue.java new file mode 100644 index 000000000..6ff5f157e --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/UniqueValue.java @@ -0,0 +1,119 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.internet; + +import java.net.*; +import java.util.concurrent.atomic.AtomicInteger; +import javax.mail.Session; + +/** + * This is a utility class that generates unique values. The generated + * String contains only US-ASCII characters and hence is safe for use + * in RFC822 headers.

    + * + * This is a package private class. + * + * @author John Mani + * @author Max Spivak + * @author Bill Shannon + */ + +class UniqueValue { + /** + * A global unique number, to ensure uniqueness of generated strings. + */ + private static AtomicInteger id = new AtomicInteger(); + + /** + * Get a unique value for use in a multipart boundary string. + * + * This implementation generates it by concatenating a global + * part number, a newly created object's hashCode(), + * and the current time (in milliseconds). + */ + public static String getUniqueBoundaryValue() { + StringBuilder s = new StringBuilder(); + long hash = s.hashCode(); + + // Unique string is ----=_Part__. + s.append("----=_Part_").append(id.getAndIncrement()).append("_"). + append(hash).append('.'). + append(System.currentTimeMillis()); + return s.toString(); + } + + /** + * Get a unique value for use in a Message-ID. + * + * This implementation generates it by concatenating a newly + * created object's hashCode(), a global ID + * (incremented on every use), the current time (in milliseconds), + * and the host name from this user's local address generated by + * InternetAddress.getLocalAddress(). + * (The host name defaults to "localhost" if + * getLocalAddress() returns null.) + * + * @param ssn Session object used to get the local address + * @see javax.mail.internet.InternetAddress + */ + public static String getUniqueMessageIDValue(Session ssn) { + String suffix = null; + + InternetAddress addr = InternetAddress.getLocalAddress(ssn); + if (addr != null) + suffix = addr.getAddress(); + else { + suffix = "javamailuser@localhost"; // worst-case default + } + int at = suffix.lastIndexOf('@'); + if (at >= 0) + suffix = suffix.substring(at); + + StringBuilder s = new StringBuilder(); + + // Unique string is .. + s.append(s.hashCode()).append('.'). + append(id.getAndIncrement()).append('.'). + append(System.currentTimeMillis()). + append(suffix); + return s.toString(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/internet/package.html b/fine-third-default/fine-mail/src/javax/mail/internet/package.html new file mode 100644 index 000000000..4309a642f --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/internet/package.html @@ -0,0 +1,572 @@ + + + + + + +javax.mail.internet package + + + +

    +Classes specific to Internet mail systems. +This package supports features that are specific to Internet mail systems +based on the MIME standard +(RFC 2045, +RFC 2046, and +RFC 2047). +The IMAP, SMTP, and POP3 protocols use +{@link javax.mail.internet.MimeMessage MimeMessages}. +

    +Properties +

    +The JavaMail API supports the following standard properties, +which may be set in the Session object, or in the +Properties object used to create the Session object. +The properties are always set as strings; the Type column describes +how the string is interpreted. For example, use +

    +
    +	session.setProperty("mail.mime.address.strict", "false");
    +
    +

    +to set the mail.mime.address.strict property, +which is of type boolean. +

    + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mail.mime.address.strictboolean +The mail.mime.address.strict session property controls +the parsing of address headers. By default, strict parsing of address +headers is done. If this property is set to "false", +strict parsing is not done and many illegal addresses that sometimes +occur in real messages are allowed. See the InternetAddress +class for details. +
    mail.mime.allowutf8boolean +If set to "true", UTF-8 strings are allowed in message headers, +e.g., in addresses. This should only be set if the mail server also +supports UTF-8. +
    +

    +The JavaMail API specification requires support for the following properties, +which must be set in the System properties. +The properties are always set as strings; the Type column describes +how the string is interpreted. For example, use +

    +
    +	System.setProperty("mail.mime.decodetext.strict", "false");
    +
    +

    +to set the mail.mime.decodetext.strict property, +which is of type boolean. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mail.mime.charsetString +The mail.mime.charset System property can +be used to specify the default MIME charset to use for encoded words +and text parts that don't otherwise specify a charset. Normally, the +default MIME charset is derived from the default Java charset, as +specified in the file.encoding System property. Most +applications will have no need to explicitly set the default MIME +charset. In cases where the default MIME charset to be used for +mail messages is different than the charset used for files stored on +the system, this property should be set. +
    mail.mime.decodetext.strictboolean +The mail.mime.decodetext.strict property controls +decoding of MIME encoded words. The MIME spec requires that encoded +words start at the beginning of a whitespace separated word. Some +mailers incorrectly include encoded words in the middle of a word. +If the mail.mime.decodetext.strict System property is +set to "false", an attempt will be made to decode these +illegal encoded words. The default is true. +
    mail.mime.encodeeol.strictboolean +The mail.mime.encodeeol.strict property controls the +choice of Content-Transfer-Encoding for MIME parts that are not of +type "text". Often such parts will contain textual data for which +an encoding that allows normal end of line conventions is appropriate. +In rare cases, such a part will appear to contain entirely textual +data, but will require an encoding that preserves CR and LF characters +without change. If the mail.mime.encodeeol.strict +System property is set to "true", such an encoding will +be used when necessary. The default is false. +
    mail.mime.decodefilenameboolean +If set to "true", the getFileName method +uses the MimeUtility +method decodeText to decode any +non-ASCII characters in the filename. Note that this decoding +violates the MIME specification, but is useful for interoperating +with some mail clients that use this convention. +The default is false. +
    mail.mime.encodefilenameboolean +If set to "true", the setFileName method +uses the MimeUtility +method encodeText to encode any +non-ASCII characters in the filename. Note that this encoding +violates the MIME specification, but is useful for interoperating +with some mail clients that use this convention. +The default is false. +
    mail.mime.decodeparametersboolean +If set to "false", non-ASCII parameters in a +ParameterList, e.g., in a Content-Type header, +will not be decoded as specified by +RFC 2231. +The default is true. +
    mail.mime.encodeparametersboolean +If set to "false", non-ASCII parameters in a +ParameterList, e.g., in a Content-Type header, +will not be encoded as specified by +RFC 2231. +The default is true. +
    mail.mime.multipart. ignoremissingendboundaryboolean +Normally, when parsing a multipart MIME message, a message that is +missing the final end boundary line is not considered an error. +The data simply ends at the end of the input. Note that messages +of this form violate the MIME specification. If the property +mail.mime.multipart.ignoremissingendboundary is set +to false, such messages are considered an error and a +MesagingException will be thrown when parsing such a +message. +
    mail.mime.multipart. ignoremissingboundaryparameterboolean +If the Content-Type header for a multipart content does not have +a boundary parameter, the multipart parsing code +will look for the first line in the content that looks like a +boundary line and extract the boundary parameter from the line. +If this property is set to "false", a +MessagingException will be thrown if the Content-Type +header doesn't specify a boundary parameter. +The default is true. +
    mail.mime.multipart. ignoreexistingboundaryparameterboolean +Normally the boundary parameter in the Content-Type header of a multipart +body part is used to specify the separator between parts of the multipart +body. This System property may be set to "true" to cause +the parser to look for a line in the multipart body that looks like a +boundary line and use that value as the separator between subsequent parts. +This may be useful in cases where a broken anti-virus product has rewritten +the message incorrectly such that the boundary parameter and the actual +boundary value no longer match. +The default value of this property is false. +
    mail.mime.multipart. allowemptyboolean +Normally, when writing out a MimeMultipart that contains no body +parts, or when trying to parse a multipart message with no body parts, +a MessagingException is thrown. The MIME spec does not allow +multipart content with no body parts. This +System property may be set to "true" to override this behavior. +When writing out such a MimeMultipart, a single empty part will be +included. When reading such a multipart, a MimeMultipart will be created +with no body parts. +The default value of this property is false. +
    + + +

    +The following properties are supported by the reference implementation (RI) of +JavaMail, but are not currently a required part of the specification. +These must be set as Session properties. +The names, types, defaults, and semantics of these properties may +change in future releases. +

    + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mail.alternatesString +A string containing other email addresses that the current user is known by. +The MimeMessage reply method will eliminate any +of these addresses from the recipient list in the message it constructs, +to avoid sending the reply back to the sender. +
    mail.replyallccboolean +If set to "true", the MimeMessage +reply method will put all recipients except the original +sender in the Cc list of the newly constructed message. +Normally, recipients in the To header of the original +message will also appear in the To list of the newly +constructed message. +
    + +

    +The following properties are supported by the reference implementation (RI) of +JavaMail, but are not currently a required part of the specification. +These must be set as System properties. +The names, types, defaults, and semantics of these properties may +change in future releases. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mail.mime.base64.ignoreerrorsboolean +If set to "true", the BASE64 decoder will ignore errors +in the encoded data, returning EOF. This may be useful when dealing +with improperly encoded messages that contain extraneous data at the +end of the encoded stream. Note however that errors anywhere in the +stream will cause the decoder to stop decoding so this should be used +with extreme caution. The default is false. +
    mail.mime.foldtextboolean +If set to "true", header fields containing just text +such as the Subject and Content-Description +header fields, and long parameter values in structured headers such +as Content-Type will be folded (broken into 76 character lines) +when set and unfolded when read. The default is true. +
    mail.mime.setcontenttypefilenameboolean +If set to "true", the setFileName method +will also set the name parameter on the Content-Type +header to the specified filename. This supports interoperability with +some old mail clients. The default is true. +
    mail.mime.setdefaulttextcharsetboolean +When updating the headers of a message, a body +part with a text content type but no charset +parameter will have a charset parameter added to it +if this property is set to "true". +The default is true. +
    mail.mime.parameters.strictboolean +If set to false, when reading a message, parameter values in header fields +such as Content-Type and Content-Disposition +are allowed to contain whitespace and other special characters without +being quoted; the parameter value ends at the next semicolon. +If set to true (the default), parameter values are required to conform +to the MIME specification and must be quoted if they contain whitespace +or special characters. +
    mail.mime.applefilenamesboolean +Apple Mail incorrectly encodes filenames that contain spaces, +forgetting to quote the parameter value. If this property is +set to "true", JavaMail will try to detect this +situation when parsing parameters and work around it. +The default is false. +Note that this property handles a subset of the cases handled +by setting the mail.mime.parameters.strict property to false. +This property will likely be removed in a future release. +
    mail.mime.windowsfilenamesboolean +Internet Explorer 6 incorrectly includes a complete pathname +in the filename parameter of the Content-Disposition header +for uploaded files, and fails to properly escape the backslashes +in the pathname. If this property is +set to "true", JavaMail will preserve all backslashes +in the "filename" and "name" parameters of any MIME header. +The default is false. +Note that this is a violation of the MIME specification but may +be useful when using JavaMail to parse HTTP messages for uploaded +files sent by IE6. +
    mail.mime. ignoreunknownencodingboolean +If set to "true", an unknown value in the +Content-Transfer-Encoding header will be ignored +when reading a message and an encoding of "8bit" will be assumed. +If set to "false", an exception is thrown for an +unknown encoding value. The default is false. +
    mail.mime.uudecode. ignoreerrorsboolean +If set to "true", errors in the encoded format of a +uuencoded document will be ignored when reading a message part. +If set to "false", an exception is thrown for an +incorrectly encoded message part. The default is false. +
    mail.mime.uudecode. ignoremissingbeginendboolean +If set to "true", a missing "being" or "end" line in a +uuencoded document will be ignored when reading a message part. +If set to "false", an exception is thrown for a +uuencoded message part without the required "begin" and "end" lines. +The default is false. +
    mail.mime. ignorewhitespacelinesboolean +Normally the header of a MIME part is separated from the body by an empty +line. This System property may be set to "true" to cause +the parser to consider a line containing only whitespace to be an empty +line. The default value of this property is false. +
    mail.mime. ignoremultipartencodingboolean +The MIME spec does not allow body parts of type multipart/* to be encoded. +The Content-Transfer-Encoding header is ignored in this case. +Setting this System property to "false" will +cause the Content-Transfer-Encoding header to be honored for multipart +content. +The default value of this property is true. +
    mail.mime.allowencodedmessagesboolean +The MIME spec does not allow body parts of type message/* to be encoded. +The Content-Transfer-Encoding header is ignored in this case. +Some versions of Microsoft Outlook will incorrectly encode message +attachments. Setting this System property to "true" will +cause the Content-Transfer-Encoding header to be honored for message +attachments. +The default value of this property is false. +
    mail.mime.contenttypehandlerString +In some cases JavaMail is unable to process messages with an invalid +Content-Type header. The header may have incorrect syntax or other +problems. This property specifies the name of a class that will be +used to clean up the Content-Type header value before JavaMail uses it. +The class must have a method with this signature: +public static String cleanContentType(MimePart mp, String contentType) +Whenever JavaMail accesses the Content-Type header of a message, it +will pass the value to this method and use the returned value instead. +The value may be null if the Content-Type header isn't present. +Returning null will cause the default Content-Type to be used. +The MimePart may be used to access other headers of the message part +to determine how to correct the Content-Type. +Note that the Content-Type handler doesn't affect the +getHeader method, which still returns the raw header value. +Note also that the handler doesn't affect the IMAP provider; the IMAP +server is responsible for returning pre-parsed, syntactically correct +Content-Type information. +
    mail.mime.address.usecanonicalhostnameboolean +Use the +{@link java.net.InetAddress#getCanonicalHostName InetAddress.getCanonicalHostName} +method to determine the host name in the +{@link javax.mail.internet.InternetAddress#getLocalAddress InternetAddress.getLocalAddress} +method. +With some network configurations, InetAddress.getCanonicalHostName may be +slow or may return an address instead of a host name. +In that case, setting this System property to false will cause the +{@link java.net.InetAddress#getHostName InetAddress.getHostName} +method to be used instead. +The default is true. +
    +

    +The current +implementation of classes in this package log debugging information using +{@link java.util.logging.Logger} as described in the following table: +

    + + + + + + + + + + + + +
    Logger NameLogging LevelPurpose
    javax.mail.internetFINEGeneral debugging output
    + + + diff --git a/fine-third-default/fine-mail/src/javax/mail/package.html b/fine-third-default/fine-mail/src/javax/mail/package.html new file mode 100644 index 000000000..58d464d49 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/package.html @@ -0,0 +1,371 @@ + + + + + + +javax.mail package + + + +

    +The JavaMail™ API +provides classes that model a mail system. +The javax.mail package defines classes that are common to +all mail systems. +The javax.mail.internet package defines classes that are specific +to mail systems based on internet standards such as MIME, SMTP, POP3, and IMAP. +The JavaMail API includes the javax.mail package and subpackages. +

    +

    +For an overview of the JavaMail API, read the + +JavaMail specification. +

    +

    +The code to send a plain text message can be as simple as the following: +

    +
    +    Properties props = new Properties();
    +    props.put("mail.smtp.host", "my-mail-server");
    +    Session session = Session.getInstance(props, null);
    +
    +    try {
    +	MimeMessage msg = new MimeMessage(session);
    +	msg.setFrom("me@example.com");
    +	msg.setRecipients(Message.RecipientType.TO,
    +			  "you@example.com");
    +	msg.setSubject("JavaMail hello world example");
    +	msg.setSentDate(new Date());
    +	msg.setText("Hello, world!\n");
    +	Transport.send(msg, "me@example.com", "my-password");
    +    } catch (MessagingException mex) {
    +	System.out.println("send failed, exception: " + mex);
    +    }
    +
    +

    +The JavaMail download bundle contains many more complete examples +in the "demo" directory. +

    +

    +Don't forget to see the + +JavaMail API FAQ +for answers to the most common questions. +The +JavaMail web site +contains many additional resources. +

    +Properties +

    +The JavaMail API supports the following standard properties, +which may be set in the Session object, or in the +Properties object used to create the Session object. +The properties are always set as strings; the Type column describes +how the string is interpreted. For example, use +

    +
    +	props.put("mail.debug", "true");
    +
    +

    +to set the mail.debug property, which is of type boolean. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mail.debugboolean +The initial debug mode. +Default is false. +
    mail.fromString +The return email address of the current user, used by the +InternetAddress method getLocalAddress. +
    mail.mime.address.strictboolean +The MimeMessage class uses the InternetAddress method +parseHeader to parse headers in messages. This property +controls the strict flag passed to the parseHeader +method. The default is true. +
    mail.hostString +The default host name of the mail server for both Stores and Transports. +Used if the mail.protocol.host property isn't set. +
    mail.store.protocolString +Specifies the default message access protocol. The +Session method getStore() returns a Store +object that implements this protocol. By default the first Store +provider in the configuration files is returned. +
    mail.transport.protocolString +Specifies the default message transport protocol. The +Session method getTransport() returns a Transport +object that implements this protocol. By default the first Transport +provider in the configuration files is returned. +
    mail.userString +The default user name to use when connecting to the mail server. +Used if the mail.protocol.user property isn't set. +
    mail.protocol.classString +Specifies the fully qualified class name of the provider for the +specified protocol. Used in cases where more than one provider +for a given protocol exists; this property can be used to specify +which provider to use by default. The provider must still be listed +in a configuration file. +
    mail.protocol.hostString +The host name of the mail server for the specified protocol. +Overrides the mail.host property. +
    mail.protocol.portint +The port number of the mail server for the specified protocol. +If not specified the protocol's default port number is used. +
    mail.protocol.userString +The user name to use when connecting to mail servers +using the specified protocol. +Overrides the mail.user property. +
    + +

    +The following properties are supported by the reference implementation (RI) of +JavaMail, but are not currently a required part of the specification. +The names, types, defaults, and semantics of these properties may +change in future releases. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    mail.debug.authboolean +Include protocol authentication commands (including usernames and passwords) +in the debug output. +Default is false. +
    mail.debug.auth.usernameboolean +Include the user name in non-protocol debug output. +Default is true. +
    mail.debug.auth.passwordboolean +Include the password in non-protocol debug output. +Default is false. +
    mail.transport.protocol.address-typeString +Specifies the default message transport protocol for the specified address type. +The Session method getTransport(Address) returns a +Transport object that implements this protocol when the address is of the +specified type (e.g., "rfc822" for standard internet addresses). +By default the first Transport configured for that address type is used. +This property can be used to override the behavior of the +{@link javax.mail.Transport#send send} method of the +{@link javax.mail.Transport Transport} class so that (for example) the "smtps" +protocol is used instead of the "smtp" protocol by setting the property +mail.transport.protocol.rfc822 to "smtps". +
    mail.event.scopeString +Controls the scope of events. (See the javax.mail.event package.) +By default, a separate event queue and thread is used for events for each +Store, Transport, or Folder. +If this property is set to "session", all such events are put in a single +event queue processed by a single thread for the current session. +If this property is set to "application", all such events are put in a single +event queue processed by a single thread for the current application. +(Applications are distinguished by their context class loader.) +
    mail.event.executorjava.util.concurrent.Executor +By default, a new Thread is created for each event queue. +This thread is used to call the listeners for these events. +If this property is set to an instance of an Executor, the +Executor.execute method is used to run the event dispatcher +for an event queue. The event dispatcher runs until the +event queue is no longer in use. +
    + +

    +The JavaMail API also supports several System properties; +see the {@link javax.mail.internet} package documentation +for details. +

    +

    +The JavaMail reference +implementation includes protocol providers in subpackages of +com.sun.mail. Note that the APIs to these protocol +providers are not part of the standard JavaMail API. Portable +programs will not use these APIs. +

    +

    +Nonportable programs may use the APIs of the protocol providers +by (for example) casting a returned Folder object to a +com.sun.mail.imap.IMAPFolder object. Similarly for +Store and Message objects returned from the +standard JavaMail APIs. +

    +

    +The protocol providers also support properties that are specific to +those providers. The package documentation for the +{@link com.sun.mail.imap IMAP}, {@link com.sun.mail.pop3 POP3}, +and {@link com.sun.mail.smtp SMTP} packages provide details. +

    +

    +In addition to printing debugging output as controlled by the +{@link javax.mail.Session Session} configuration, the current +implementation of classes in this package log the same information using +{@link java.util.logging.Logger} as described in the following table: +

    + + + + + + + + + + + + + + + + + + +
    Logger NameLogging LevelPurpose
    javax.mailCONFIGConfiguration of the Session
    javax.mailFINEGeneral debugging output
    + + + diff --git a/fine-third-default/fine-mail/src/javax/mail/search/AddressStringTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/AddressStringTerm.java new file mode 100644 index 000000000..13233bd3b --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/AddressStringTerm.java @@ -0,0 +1,104 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; +import javax.mail.Address; +import javax.mail.internet.InternetAddress; + +/** + * This abstract class implements string comparisons for Message + * addresses.

    + * + * Note that this class differs from the AddressTerm class + * in that this class does comparisons on address strings rather than + * Address objects. + * + * @since JavaMail 1.1 + */ + +public abstract class AddressStringTerm extends StringTerm { + + private static final long serialVersionUID = 3086821234204980368L; + + /** + * Constructor. + * + * @param pattern the address pattern to be compared. + */ + protected AddressStringTerm(String pattern) { + super(pattern, true); // we need case-insensitive comparison. + } + + /** + * Check whether the address pattern specified in the constructor is + * a substring of the string representation of the given Address + * object.

    + * + * Note that if the string representation of the given Address object + * contains charset or transfer encodings, the encodings must be + * accounted for, during the match process.

    + * + * @param a The comparison is applied to this Address object. + * @return true if the match succeeds, otherwise false. + */ + protected boolean match(Address a) { + if (a instanceof InternetAddress) { + InternetAddress ia = (InternetAddress)a; + // We dont use toString() to get "a"'s String representation, + // because InternetAddress.toString() returns a RFC 2047 + // encoded string, which isn't what we need here. + + return super.match(ia.toUnicodeString()); + } else + return super.match(a.toString()); + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AddressStringTerm)) + return false; + return super.equals(obj); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/AddressTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/AddressTerm.java new file mode 100644 index 000000000..211fc5c42 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/AddressTerm.java @@ -0,0 +1,103 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Address; + +/** + * This class implements Message Address comparisons. + * + * @author Bill Shannon + * @author John Mani + */ + +public abstract class AddressTerm extends SearchTerm { + /** + * The address. + * + * @serial + */ + protected Address address; + + private static final long serialVersionUID = 2005405551929769980L; + + protected AddressTerm(Address address) { + this.address = address; + } + + /** + * Return the address to match with. + * + * @return the adddress + */ + public Address getAddress() { + return address; + } + + /** + * Match against the argument Address. + * + * @param a the address to match + * @return true if it matches + */ + protected boolean match(Address a) { + return (a.equals(address)); + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AddressTerm)) + return false; + AddressTerm at = (AddressTerm)obj; + return at.address.equals(this.address); + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return address.hashCode(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/AndTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/AndTerm.java new file mode 100644 index 000000000..27a886813 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/AndTerm.java @@ -0,0 +1,140 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; + +/** + * This class implements the logical AND operator on individual + * SearchTerms. + * + * @author Bill Shannon + * @author John Mani + */ +public final class AndTerm extends SearchTerm { + + /** + * The array of terms on which the AND operator should be + * applied. + * + * @serial + */ + private SearchTerm[] terms; + + private static final long serialVersionUID = -3583274505380989582L; + + /** + * Constructor that takes two terms. + * + * @param t1 first term + * @param t2 second term + */ + public AndTerm(SearchTerm t1, SearchTerm t2) { + terms = new SearchTerm[2]; + terms[0] = t1; + terms[1] = t2; + } + + /** + * Constructor that takes an array of SearchTerms. + * + * @param t array of terms + */ + public AndTerm(SearchTerm[] t) { + terms = new SearchTerm[t.length]; // clone the array + for (int i = 0; i < t.length; i++) + terms[i] = t[i]; + } + + /** + * Return the search terms. + * + * @return the search terms + */ + public SearchTerm[] getTerms() { + return terms.clone(); + } + + /** + * The AND operation.

    + * + * The terms specified in the constructor are applied to + * the given object and the AND operator is applied to their results. + * + * @param msg The specified SearchTerms are applied to this Message + * and the AND operator is applied to their results. + * @return true if the AND succeds, otherwise false + */ + @Override + public boolean match(Message msg) { + for (int i=0; i < terms.length; i++) + if (!terms[i].match(msg)) + return false; + return true; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AndTerm)) + return false; + AndTerm at = (AndTerm)obj; + if (at.terms.length != terms.length) + return false; + for (int i=0; i < terms.length; i++) + if (!terms[i].equals(at.terms[i])) + return false; + return true; + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + int hash = 0; + for (int i=0; i < terms.length; i++) + hash += terms[i].hashCode(); + return hash; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/BodyTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/BodyTerm.java new file mode 100644 index 000000000..3633facc4 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/BodyTerm.java @@ -0,0 +1,127 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import java.io.IOException; +import javax.mail.*; + +/** + * This class implements searches on a message body. + * All parts of the message that are of MIME type "text/*" are searched. + * The pattern is a simple string that must appear as a substring in + * the message body. + * + * @author Bill Shannon + * @author John Mani + */ +public final class BodyTerm extends StringTerm { + + private static final long serialVersionUID = -4888862527916911385L; + + /** + * Constructor + * @param pattern The String to search for + */ + public BodyTerm(String pattern) { + // Note: comparison is case-insensitive + super(pattern); + } + + /** + * The match method. + * + * @param msg The pattern search is applied on this Message's body + * @return true if the pattern is found; otherwise false + */ + @Override + public boolean match(Message msg) { + return matchPart(msg); + } + + /** + * Search all the parts of the message for any text part + * that matches the pattern. + */ + private boolean matchPart(Part p) { + try { + /* + * Using isMimeType to determine the content type avoids + * fetching the actual content data until we need it. + */ + if (p.isMimeType("text/*")) { + String s = (String)p.getContent(); + if (s == null) + return false; + /* + * We invoke our superclass' (i.e., StringTerm) match method. + * Note however that StringTerm.match() is not optimized + * for substring searches in large string buffers. We really + * need to have a StringTerm subclass, say BigStringTerm, + * with its own match() method that uses a better algorithm .. + * and then subclass BodyTerm from BigStringTerm. + */ + return super.match(s); + } else if (p.isMimeType("multipart/*")) { + Multipart mp = (Multipart)p.getContent(); + int count = mp.getCount(); + for (int i = 0; i < count; i++) + if (matchPart(mp.getBodyPart(i))) + return true; + } else if (p.isMimeType("message/rfc822")) { + return matchPart((Part)p.getContent()); + } + } catch (MessagingException ex) { + } catch (IOException ex) { + } catch (RuntimeException ex) { + } + return false; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BodyTerm)) + return false; + return super.equals(obj); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/ComparisonTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/ComparisonTerm.java new file mode 100644 index 000000000..7b1cd2e8a --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/ComparisonTerm.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +/** + * This class models the comparison operator. This is an abstract + * class; subclasses implement comparisons for different datatypes. + * + * @author Bill Shannon + * @author John Mani + */ +public abstract class ComparisonTerm extends SearchTerm { + public static final int LE = 1; + public static final int LT = 2; + public static final int EQ = 3; + public static final int NE = 4; + public static final int GT = 5; + public static final int GE = 6; + + /** + * The comparison. + * + * @serial + */ + protected int comparison; + + private static final long serialVersionUID = 1456646953666474308L; + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ComparisonTerm)) + return false; + ComparisonTerm ct = (ComparisonTerm)obj; + return ct.comparison == this.comparison; + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return comparison; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/DateTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/DateTerm.java new file mode 100644 index 000000000..5da193325 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/DateTerm.java @@ -0,0 +1,132 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import java.util.Date; + +/** + * This class implements comparisons for Dates + * + * @author Bill Shannon + * @author John Mani + */ +public abstract class DateTerm extends ComparisonTerm { + /** + * The date. + * + * @serial + */ + protected Date date; + + private static final long serialVersionUID = 4818873430063720043L; + + /** + * Constructor. + * @param comparison the comparison type + * @param date The Date to be compared against + */ + protected DateTerm(int comparison, Date date) { + this.comparison = comparison; + this.date = date; + } + + /** + * Return the Date to compare with. + * + * @return the date + */ + public Date getDate() { + return new Date(date.getTime()); + } + + /** + * Return the type of comparison. + * + * @return the comparison type + */ + public int getComparison() { + return comparison; + } + + /** + * The date comparison method. + * + * @param d the date in the constructor is compared with this date + * @return true if the dates match, otherwise false + */ + protected boolean match(Date d) { + switch (comparison) { + case LE: + return d.before(date) || d.equals(date); + case LT: + return d.before(date); + case EQ: + return d.equals(date); + case NE: + return !d.equals(date); + case GT: + return d.after(date); + case GE: + return d.after(date) || d.equals(date); + default: + return false; + } + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DateTerm)) + return false; + DateTerm dt = (DateTerm)obj; + return dt.date.equals(this.date) && super.equals(obj); + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return date.hashCode() + super.hashCode(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/FlagTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/FlagTerm.java new file mode 100644 index 000000000..d4bd269e3 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/FlagTerm.java @@ -0,0 +1,168 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.*; + +/** + * This class implements comparisons for Message Flags. + * + * @author Bill Shannon + * @author John Mani + */ +public final class FlagTerm extends SearchTerm { + + /** + * Indicates whether to test for the presence or + * absence of the specified Flag. If true, + * then test whether all the specified flags are present, else + * test whether all the specified flags are absent. + * + * @serial + */ + private boolean set; + + /** + * Flags object containing the flags to test. + * + * @serial + */ + private Flags flags; + + private static final long serialVersionUID = -142991500302030647L; + + /** + * Constructor. + * + * @param flags Flags object containing the flags to check for + * @param set the flag setting to check for + */ + public FlagTerm(Flags flags, boolean set) { + this.flags = flags; + this.set = set; + } + + /** + * Return the Flags to test. + * + * @return the flags + */ + public Flags getFlags() { + return (Flags)flags.clone(); + } + + /** + * Return true if testing whether the flags are set. + * + * @return true if testing whether the flags are set + */ + public boolean getTestSet() { + return set; + } + + /** + * The comparison method. + * + * @param msg The flag comparison is applied to this Message + * @return true if the comparson succeeds, otherwise false. + */ + @Override + public boolean match(Message msg) { + + try { + Flags f = msg.getFlags(); + if (set) { // This is easy + if (f.contains(flags)) + return true; + else + return false; + } + + // Return true if ALL flags in the passed in Flags + // object are NOT set in this Message. + + // Got to do this the hard way ... + Flags.Flag[] sf = flags.getSystemFlags(); + + // Check each flag in the passed in Flags object + for (int i = 0; i < sf.length; i++) { + if (f.contains(sf[i])) + // this flag IS set in this Message, get out. + return false; + } + + String[] s = flags.getUserFlags(); + + // Check each flag in the passed in Flags object + for (int i = 0; i < s.length; i++) { + if (f.contains(s[i])) + // this flag IS set in this Message, get out. + return false; + } + + return true; + + } catch (MessagingException e) { + return false; + } catch (RuntimeException e) { + return false; + } + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof FlagTerm)) + return false; + FlagTerm ft = (FlagTerm)obj; + return ft.set == this.set && ft.flags.equals(this.flags); + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return set ? flags.hashCode() : ~flags.hashCode(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/FromStringTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/FromStringTerm.java new file mode 100644 index 000000000..07981d849 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/FromStringTerm.java @@ -0,0 +1,106 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; +import javax.mail.Address; + +/** + * This class implements string comparisons for the From Address + * header.

    + * + * Note that this class differs from the FromTerm class + * in that this class does comparisons on address strings rather than Address + * objects. The string comparisons are case-insensitive. + * + * @since JavaMail 1.1 + */ + +public final class FromStringTerm extends AddressStringTerm { + + private static final long serialVersionUID = 5801127523826772788L; + + /** + * Constructor. + * + * @param pattern the address pattern to be compared. + */ + public FromStringTerm(String pattern) { + super(pattern); + } + + /** + * Check whether the address string specified in the constructor is + * a substring of the From address of this Message. + * + * @param msg The comparison is applied to this Message's From + * address. + * @return true if the match succeeds, otherwise false. + */ + @Override + public boolean match(Message msg) { + Address[] from; + + try { + from = msg.getFrom(); + } catch (Exception e) { + return false; + } + + if (from == null) + return false; + + for (int i=0; i < from.length; i++) + if (super.match(from[i])) + return true; + return false; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof FromStringTerm)) + return false; + return super.equals(obj); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/FromTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/FromTerm.java new file mode 100644 index 000000000..5ae2a354c --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/FromTerm.java @@ -0,0 +1,98 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; +import javax.mail.Address; + +/** + * This class implements comparisons for the From Address header. + * + * @author Bill Shannon + * @author John Mani + */ +public final class FromTerm extends AddressTerm { + + private static final long serialVersionUID = 5214730291502658665L; + + /** + * Constructor + * @param address The Address to be compared + */ + public FromTerm(Address address) { + super(address); + } + + /** + * The address comparator. + * + * @param msg The address comparison is applied to this Message + * @return true if the comparison succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + Address[] from; + + try { + from = msg.getFrom(); + } catch (Exception e) { + return false; + } + + if (from == null) + return false; + + for (int i=0; i < from.length; i++) + if (super.match(from[i])) + return true; + return false; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof FromTerm)) + return false; + return super.equals(obj); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/HeaderTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/HeaderTerm.java new file mode 100644 index 000000000..2d5f8d14c --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/HeaderTerm.java @@ -0,0 +1,129 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import java.util.Locale; +import javax.mail.Message; + +/** + * This class implements comparisons for Message headers. + * The comparison is case-insensitive. + * + * @author Bill Shannon + * @author John Mani + */ +public final class HeaderTerm extends StringTerm { + /** + * The name of the header. + * + * @serial + */ + private String headerName; + + private static final long serialVersionUID = 8342514650333389122L; + + /** + * Constructor. + * + * @param headerName The name of the header + * @param pattern The pattern to search for + */ + public HeaderTerm(String headerName, String pattern) { + super(pattern); + this.headerName = headerName; + } + + /** + * Return the name of the header to compare with. + * + * @return the name of the header + */ + public String getHeaderName() { + return headerName; + } + + /** + * The header match method. + * + * @param msg The match is applied to this Message's header + * @return true if the match succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + String[] headers; + + try { + headers = msg.getHeader(headerName); + } catch (Exception e) { + return false; + } + + if (headers == null) + return false; + + for (int i=0; i < headers.length; i++) + if (super.match(headers[i])) + return true; + return false; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof HeaderTerm)) + return false; + HeaderTerm ht = (HeaderTerm)obj; + // XXX - depends on header comparisons being case independent + return ht.headerName.equalsIgnoreCase(headerName) && super.equals(ht); + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + // XXX - depends on header comparisons being case independent + return headerName.toLowerCase(Locale.ENGLISH).hashCode() + + super.hashCode(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/IntegerComparisonTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/IntegerComparisonTerm.java new file mode 100644 index 000000000..ce2e8e539 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/IntegerComparisonTerm.java @@ -0,0 +1,119 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +/** + * This class implements comparisons for integers. + * + * @author Bill Shannon + * @author John Mani + */ +public abstract class IntegerComparisonTerm extends ComparisonTerm { + /** + * The number. + * + * @serial + */ + protected int number; + + private static final long serialVersionUID = -6963571240154302484L; + + protected IntegerComparisonTerm(int comparison, int number) { + this.comparison = comparison; + this.number = number; + } + + /** + * Return the number to compare with. + * + * @return the number + */ + public int getNumber() { + return number; + } + + /** + * Return the type of comparison. + * + * @return the comparison type + */ + public int getComparison() { + return comparison; + } + + protected boolean match(int i) { + switch (comparison) { + case LE: + return i <= number; + case LT: + return i < number; + case EQ: + return i == number; + case NE: + return i != number; + case GT: + return i > number; + case GE: + return i >= number; + default: + return false; + } + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof IntegerComparisonTerm)) + return false; + IntegerComparisonTerm ict = (IntegerComparisonTerm)obj; + return ict.number == this.number && super.equals(obj); + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return number + super.hashCode(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/MessageIDTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/MessageIDTerm.java new file mode 100644 index 000000000..cee431764 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/MessageIDTerm.java @@ -0,0 +1,105 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; + +/** + * This term models the RFC822 "MessageId" - a message-id for + * Internet messages that is supposed to be unique per message. + * Clients can use this term to search a folder for a message given + * its MessageId.

    + * + * The MessageId is represented as a String. + * + * @author Bill Shannon + * @author John Mani + */ +public final class MessageIDTerm extends StringTerm { + + private static final long serialVersionUID = -2121096296454691963L; + + /** + * Constructor. + * + * @param msgid the msgid to search for + */ + public MessageIDTerm(String msgid) { + // Note: comparison is case-insensitive + super(msgid); + } + + /** + * The match method. + * + * @param msg the match is applied to this Message's + * Message-ID header + * @return true if the match succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + String[] s; + + try { + s = msg.getHeader("Message-ID"); + } catch (Exception e) { + return false; + } + + if (s == null) + return false; + + for (int i=0; i < s.length; i++) + if (super.match(s[i])) + return true; + return false; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MessageIDTerm)) + return false; + return super.equals(obj); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/MessageNumberTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/MessageNumberTerm.java new file mode 100644 index 000000000..576062d0a --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/MessageNumberTerm.java @@ -0,0 +1,92 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; + +/** + * This class implements comparisons for Message numbers. + * + * @author Bill Shannon + * @author John Mani + */ +public final class MessageNumberTerm extends IntegerComparisonTerm { + + private static final long serialVersionUID = -5379625829658623812L; + + /** + * Constructor. + * + * @param number the Message number + */ + public MessageNumberTerm(int number) { + super(EQ, number); + } + + /** + * The match method. + * + * @param msg the Message number is matched with this Message + * @return true if the match succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + int msgno; + + try { + msgno = msg.getMessageNumber(); + } catch (Exception e) { + return false; + } + + return super.match(msgno); + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MessageNumberTerm)) + return false; + return super.equals(obj); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/NotTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/NotTerm.java new file mode 100644 index 000000000..5db52fbec --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/NotTerm.java @@ -0,0 +1,98 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; + +/** + * This class implements the logical NEGATION operator. + * + * @author Bill Shannon + * @author John Mani + */ +public final class NotTerm extends SearchTerm { + /** + * The search term to negate. + * + * @serial + */ + private SearchTerm term; + + private static final long serialVersionUID = 7152293214217310216L; + + public NotTerm(SearchTerm t) { + term = t; + } + + /** + * Return the term to negate. + * + * @return the Term + */ + public SearchTerm getTerm() { + return term; + } + + /* The NOT operation */ + @Override + public boolean match(Message msg) { + return !term.match(msg); + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof NotTerm)) + return false; + NotTerm nt = (NotTerm)obj; + return nt.term.equals(this.term); + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return term.hashCode() << 1; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/OrTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/OrTerm.java new file mode 100644 index 000000000..a4a46d1bd --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/OrTerm.java @@ -0,0 +1,140 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; + +/** + * This class implements the logical OR operator on individual SearchTerms. + * + * @author Bill Shannon + * @author John Mani + */ +public final class OrTerm extends SearchTerm { + + /** + * The array of terms on which the OR operator should + * be applied. + * + * @serial + */ + private SearchTerm[] terms; + + private static final long serialVersionUID = 5380534067523646936L; + + /** + * Constructor that takes two operands. + * + * @param t1 first term + * @param t2 second term + */ + public OrTerm(SearchTerm t1, SearchTerm t2) { + terms = new SearchTerm[2]; + terms[0] = t1; + terms[1] = t2; + } + + /** + * Constructor that takes an array of SearchTerms. + * + * @param t array of search terms + */ + public OrTerm(SearchTerm[] t) { + terms = new SearchTerm[t.length]; + for (int i = 0; i < t.length; i++) + terms[i] = t[i]; + } + + /** + * Return the search terms. + * + * @return the search terms + */ + public SearchTerm[] getTerms() { + return terms.clone(); + } + + /** + * The OR operation.

    + * + * The terms specified in the constructor are applied to + * the given object and the OR operator is applied to their results. + * + * @param msg The specified SearchTerms are applied to this Message + * and the OR operator is applied to their results. + * @return true if the OR succeds, otherwise false + */ + + @Override + public boolean match(Message msg) { + for (int i=0; i < terms.length; i++) + if (terms[i].match(msg)) + return true; + return false; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof OrTerm)) + return false; + OrTerm ot = (OrTerm)obj; + if (ot.terms.length != terms.length) + return false; + for (int i=0; i < terms.length; i++) + if (!terms[i].equals(ot.terms[i])) + return false; + return true; + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + int hash = 0; + for (int i=0; i < terms.length; i++) + hash += terms[i].hashCode(); + return hash; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/ReceivedDateTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/ReceivedDateTerm.java new file mode 100644 index 000000000..c6760932a --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/ReceivedDateTerm.java @@ -0,0 +1,98 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import java.util.Date; +import javax.mail.Message; + +/** + * This class implements comparisons for the Message Received date + * + * @author Bill Shannon + * @author John Mani + */ +public final class ReceivedDateTerm extends DateTerm { + + private static final long serialVersionUID = -2756695246195503170L; + + /** + * Constructor. + * + * @param comparison the Comparison type + * @param date the date to be compared + */ + public ReceivedDateTerm(int comparison, Date date) { + super(comparison, date); + } + + /** + * The match method. + * + * @param msg the date comparator is applied to this Message's + * received date + * @return true if the comparison succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + Date d; + + try { + d = msg.getReceivedDate(); + } catch (Exception e) { + return false; + } + + if (d == null) + return false; + + return super.match(d); + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ReceivedDateTerm)) + return false; + return super.equals(obj); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/RecipientStringTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/RecipientStringTerm.java new file mode 100644 index 000000000..d464c00ac --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/RecipientStringTerm.java @@ -0,0 +1,133 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; +import javax.mail.Address; + +/** + * This class implements string comparisons for the Recipient Address + * headers.

    + * + * Note that this class differs from the RecipientTerm class + * in that this class does comparisons on address strings rather than Address + * objects. The string comparisons are case-insensitive. + * + * @since JavaMail 1.1 + */ + +public final class RecipientStringTerm extends AddressStringTerm { + + /** + * The recipient type. + * + * @serial + */ + private Message.RecipientType type; + + private static final long serialVersionUID = -8293562089611618849L; + + /** + * Constructor. + * + * @param type the recipient type + * @param pattern the address pattern to be compared. + */ + public RecipientStringTerm(Message.RecipientType type, String pattern) { + super(pattern); + this.type = type; + } + + /** + * Return the type of recipient to match with. + * + * @return the recipient type + */ + public Message.RecipientType getRecipientType() { + return type; + } + + /** + * Check whether the address specified in the constructor is + * a substring of the recipient address of this Message. + * + * @param msg The comparison is applied to this Message's recipient + * address. + * @return true if the match succeeds, otherwise false. + */ + @Override + public boolean match(Message msg) { + Address[] recipients; + + try { + recipients = msg.getRecipients(type); + } catch (Exception e) { + return false; + } + + if (recipients == null) + return false; + + for (int i=0; i < recipients.length; i++) + if (super.match(recipients[i])) + return true; + return false; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof RecipientStringTerm)) + return false; + RecipientStringTerm rst = (RecipientStringTerm)obj; + return rst.type.equals(this.type) && super.equals(obj); + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return type.hashCode() + super.hashCode(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/RecipientTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/RecipientTerm.java new file mode 100644 index 000000000..de4f59838 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/RecipientTerm.java @@ -0,0 +1,127 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; +import javax.mail.Address; + +/** + * This class implements comparisons for the Recipient Address headers. + * + * @author Bill Shannon + * @author John Mani + */ +public final class RecipientTerm extends AddressTerm { + + /** + * The recipient type. + * + * @serial + */ + private Message.RecipientType type; + + private static final long serialVersionUID = 6548700653122680468L; + + /** + * Constructor. + * + * @param type the recipient type + * @param address the address to match for + */ + public RecipientTerm(Message.RecipientType type, Address address) { + super(address); + this.type = type; + } + + /** + * Return the type of recipient to match with. + * + * @return the recipient type + */ + public Message.RecipientType getRecipientType() { + return type; + } + + /** + * The match method. + * + * @param msg The address match is applied to this Message's recepient + * address + * @return true if the match succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + Address[] recipients; + + try { + recipients = msg.getRecipients(type); + } catch (Exception e) { + return false; + } + + if (recipients == null) + return false; + + for (int i=0; i < recipients.length; i++) + if (super.match(recipients[i])) + return true; + return false; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof RecipientTerm)) + return false; + RecipientTerm rt = (RecipientTerm)obj; + return rt.type.equals(this.type) && super.equals(obj); + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return type.hashCode() + super.hashCode(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/SearchException.java b/fine-third-default/fine-mail/src/javax/mail/search/SearchException.java new file mode 100644 index 000000000..c6785699d --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/SearchException.java @@ -0,0 +1,70 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.MessagingException; + + +/** + * The exception thrown when a Search expression could not be handled. + * + * @author John Mani + */ + +public class SearchException extends MessagingException { + + private static final long serialVersionUID = -7092886778226268686L; + + /** + * Constructs a SearchException with no detail message. + */ + public SearchException() { + super(); + } + + /** + * Constructs a SearchException with the specified detail message. + * @param s the detail message + */ + public SearchException(String s) { + super(s); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/SearchTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/SearchTerm.java new file mode 100644 index 000000000..e569bd2bd --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/SearchTerm.java @@ -0,0 +1,78 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import java.io.Serializable; + +import javax.mail.Message; + +/** + * Search criteria are expressed as a tree of search-terms, forming + * a parse-tree for the search expression.

    + * + * Search-terms are represented by this class. This is an abstract + * class; subclasses implement specific match methods.

    + * + * Search terms are serializable, which allows storing a search term + * between sessions. + * + * Warning: + * Serialized objects of this class may not be compatible with future + * JavaMail API releases. The current serialization support is + * appropriate for short term storage.

    + * + * @author Bill Shannon + * @author John Mani + */ +public abstract class SearchTerm implements Serializable { + + private static final long serialVersionUID = -6652358452205992789L; + + /** + * This method applies a specific match criterion to the given + * message and returns the result. + * + * @param msg The match criterion is applied on this message + * @return true, it the match succeeds, false if the match fails + */ + + public abstract boolean match(Message msg); +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/SentDateTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/SentDateTerm.java new file mode 100644 index 000000000..470bdc568 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/SentDateTerm.java @@ -0,0 +1,98 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import java.util.Date; +import javax.mail.Message; + +/** + * This class implements comparisons for the Message SentDate. + * + * @author Bill Shannon + * @author John Mani + */ +public final class SentDateTerm extends DateTerm { + + private static final long serialVersionUID = 5647755030530907263L; + + /** + * Constructor. + * + * @param comparison the Comparison type + * @param date the date to be compared + */ + public SentDateTerm(int comparison, Date date) { + super(comparison, date); + } + + /** + * The match method. + * + * @param msg the date comparator is applied to this Message's + * sent date + * @return true if the comparison succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + Date d; + + try { + d = msg.getSentDate(); + } catch (Exception e) { + return false; + } + + if (d == null) + return false; + + return super.match(d); + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SentDateTerm)) + return false; + return super.equals(obj); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/SizeTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/SizeTerm.java new file mode 100644 index 000000000..c3ae21dbc --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/SizeTerm.java @@ -0,0 +1,96 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; + +/** + * This class implements comparisons for Message sizes. + * + * @author Bill Shannon + * @author John Mani + */ +public final class SizeTerm extends IntegerComparisonTerm { + + private static final long serialVersionUID = -2556219451005103709L; + + /** + * Constructor. + * + * @param comparison the Comparison type + * @param size the size + */ + public SizeTerm(int comparison, int size) { + super(comparison, size); + } + + /** + * The match method. + * + * @param msg the size comparator is applied to this Message's size + * @return true if the size is equal, otherwise false + */ + @Override + public boolean match(Message msg) { + int size; + + try { + size = msg.getSize(); + } catch (Exception e) { + return false; + } + + if (size == -1) + return false; + + return super.match(size); + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SizeTerm)) + return false; + return super.equals(obj); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/StringTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/StringTerm.java new file mode 100644 index 000000000..ab2fdf481 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/StringTerm.java @@ -0,0 +1,141 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +/** + * This class implements the match method for Strings. The current + * implementation provides only for substring matching. We + * could add comparisons (like strcmp ...). + * + * @author Bill Shannon + * @author John Mani + */ +public abstract class StringTerm extends SearchTerm { + /** + * The pattern. + * + * @serial + */ + protected String pattern; + + /** + * Ignore case when comparing? + * + * @serial + */ + protected boolean ignoreCase; + + private static final long serialVersionUID = 1274042129007696269L; + + /** + * Construct a StringTerm with the given pattern. + * Case will be ignored. + * + * @param pattern the pattern + */ + protected StringTerm(String pattern) { + this.pattern = pattern; + ignoreCase = true; + } + + /** + * Construct a StringTerm with the given pattern and ignoreCase flag. + * + * @param pattern the pattern + * @param ignoreCase should we ignore case? + */ + protected StringTerm(String pattern, boolean ignoreCase) { + this.pattern = pattern; + this.ignoreCase = ignoreCase; + } + + /** + * Return the string to match with. + * + * @return the string to match + */ + public String getPattern() { + return pattern; + } + + /** + * Return true if we should ignore case when matching. + * + * @return true if we should ignore case + */ + public boolean getIgnoreCase() { + return ignoreCase; + } + + protected boolean match(String s) { + int len = s.length() - pattern.length(); + for (int i=0; i <= len; i++) { + if (s.regionMatches(ignoreCase, i, + pattern, 0, pattern.length())) + return true; + } + return false; + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof StringTerm)) + return false; + StringTerm st = (StringTerm)obj; + if (ignoreCase) + return st.pattern.equalsIgnoreCase(this.pattern) && + st.ignoreCase == this.ignoreCase; + else + return st.pattern.equals(this.pattern) && + st.ignoreCase == this.ignoreCase; + } + + /** + * Compute a hashCode for this object. + */ + @Override + public int hashCode() { + return ignoreCase ? pattern.hashCode() : ~pattern.hashCode(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/SubjectTerm.java b/fine-third-default/fine-mail/src/javax/mail/search/SubjectTerm.java new file mode 100644 index 000000000..b2473d4f6 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/SubjectTerm.java @@ -0,0 +1,99 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.search; + +import javax.mail.Message; + +/** + * This class implements comparisons for the message Subject header. + * The comparison is case-insensitive. The pattern is a simple string + * that must appear as a substring in the Subject. + * + * @author Bill Shannon + * @author John Mani + */ +public final class SubjectTerm extends StringTerm { + + private static final long serialVersionUID = 7481568618055573432L; + + /** + * Constructor. + * + * @param pattern the pattern to search for + */ + public SubjectTerm(String pattern) { + // Note: comparison is case-insensitive + super(pattern); + } + + /** + * The match method. + * + * @param msg the pattern match is applied to this Message's + * subject header + * @return true if the pattern match succeeds, otherwise false + */ + @Override + public boolean match(Message msg) { + String subj; + + try { + subj = msg.getSubject(); + } catch (Exception e) { + return false; + } + + if (subj == null) + return false; + + return super.match(subj); + } + + /** + * Equality comparison. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SubjectTerm)) + return false; + return super.equals(obj); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/search/package.html b/fine-third-default/fine-mail/src/javax/mail/search/package.html new file mode 100644 index 000000000..6b925b805 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/search/package.html @@ -0,0 +1,73 @@ + + + + + + +javax.mail.search package + + + +

    +Message search terms for the JavaMail API. +This package defines classes that can be used to construct a search +expression to search a folder for messages matching the expression; +see the {@link javax.mail.Folder#search search} method on +{@link javax.mail.Folder javax.mail.Folder}. +See {@link javax.mail.search.SearchTerm SearchTerm}. +

    +

    +Note that the exact search capabilities depend on the protocol, +provider, and server in use. For the POP3 protocol, all searching is +done on the client side using the JavaMail classes. For IMAP, all +searching is done on the server side and is limited by the search +capabilities of the IMAP protocol and the IMAP server being used. +For example, IMAP date based searches have only day granularity. +

    +

    +In general, all of the string patterns supported by search terms are +just simple strings; no regular expressions are supported. +

    + + + diff --git a/fine-third-default/fine-mail/src/javax/mail/util/ByteArrayDataSource.java b/fine-third-default/fine-mail/src/javax/mail/util/ByteArrayDataSource.java new file mode 100644 index 000000000..134254089 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/util/ByteArrayDataSource.java @@ -0,0 +1,204 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.util; + +import java.io.*; +import javax.activation.*; +import javax.mail.internet.*; + +/** + * A DataSource backed by a byte array. The byte array may be + * passed in directly, or may be initialized from an InputStream + * or a String. + * + * @since JavaMail 1.4 + * @author John Mani + * @author Bill Shannon + * @author Max Spivak + */ +public class ByteArrayDataSource implements DataSource { + private byte[] data; // data + private int len = -1; + private String type; // content-type + private String name = ""; + + static class DSByteArrayOutputStream extends ByteArrayOutputStream { + public byte[] getBuf() { + return buf; + } + + public int getCount() { + return count; + } + } + + /** + * Create a ByteArrayDataSource with data from the + * specified InputStream and with the specified MIME type. + * The InputStream is read completely and the data is + * stored in a byte array. + * + * @param is the InputStream + * @param type the MIME type + * @exception IOException errors reading the stream + */ + public ByteArrayDataSource(InputStream is, String type) throws IOException { + DSByteArrayOutputStream os = new DSByteArrayOutputStream(); + byte[] buf = new byte[8192]; + int len; + while ((len = is.read(buf)) > 0) + os.write(buf, 0, len); + this.data = os.getBuf(); + this.len = os.getCount(); + + /* + * ByteArrayOutputStream doubles the size of the buffer every time + * it needs to expand, which can waste a lot of memory in the worst + * case with large buffers. Check how much is wasted here and if + * it's too much, copy the data into a new buffer and allow the + * old buffer to be garbage collected. + */ + if (this.data.length - this.len > 256*1024) { + this.data = os.toByteArray(); + this.len = this.data.length; // should be the same + } + this.type = type; + } + + /** + * Create a ByteArrayDataSource with data from the + * specified byte array and with the specified MIME type. + * + * @param data the data + * @param type the MIME type + */ + public ByteArrayDataSource(byte[] data, String type) { + this.data = data; + this.type = type; + } + + /** + * Create a ByteArrayDataSource with data from the + * specified String and with the specified MIME type. + * The MIME type should include a charset + * parameter specifying the charset to be used for the + * string. If the parameter is not included, the + * default charset is used. + * + * @param data the String + * @param type the MIME type + * @exception IOException errors reading the String + */ + public ByteArrayDataSource(String data, String type) throws IOException { + String charset = null; + try { + ContentType ct = new ContentType(type); + charset = ct.getParameter("charset"); + } catch (ParseException pex) { + // ignore parse error + } + charset = MimeUtility.javaCharset(charset); + if (charset == null) + charset = MimeUtility.getDefaultJavaCharset(); + // XXX - could convert to bytes on demand rather than copying here + this.data = data.getBytes(charset); + this.type = type; + } + + /** + * Return an InputStream for the data. + * Note that a new stream is returned each time + * this method is called. + * + * @return the InputStream + * @exception IOException if no data has been set + */ + @Override + public InputStream getInputStream() throws IOException { + if (data == null) + throw new IOException("no data"); + if (len < 0) + len = data.length; + return new SharedByteArrayInputStream(data, 0, len); + } + + /** + * Return an OutputStream for the data. + * Writing the data is not supported; an IOException + * is always thrown. + * + * @exception IOException always + */ + @Override + public OutputStream getOutputStream() throws IOException { + throw new IOException("cannot do this"); + } + + /** + * Get the MIME content type of the data. + * + * @return the MIME type + */ + @Override + public String getContentType() { + return type; + } + + /** + * Get the name of the data. + * By default, an empty string ("") is returned. + * + * @return the name of this data + */ + @Override + public String getName() { + return name; + } + + /** + * Set the name of the data. + * + * @param name the name of this data + */ + public void setName(String name) { + this.name = name; + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/util/SharedByteArrayInputStream.java b/fine-third-default/fine-mail/src/javax/mail/util/SharedByteArrayInputStream.java new file mode 100644 index 000000000..f6c056d68 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/util/SharedByteArrayInputStream.java @@ -0,0 +1,117 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.util; + +import java.io.*; +import javax.mail.internet.SharedInputStream; + +/** + * A ByteArrayInputStream that implements the SharedInputStream interface, + * allowing the underlying byte array to be shared between multiple readers. + * + * @author Bill Shannon + * @since JavaMail 1.4 + */ + +public class SharedByteArrayInputStream extends ByteArrayInputStream + implements SharedInputStream { + /** + * Position within shared buffer that this stream starts at. + */ + protected int start = 0; + + /** + * Create a SharedByteArrayInputStream representing the entire + * byte array. + * + * @param buf the byte array + */ + public SharedByteArrayInputStream(byte[] buf) { + super(buf); + } + + /** + * Create a SharedByteArrayInputStream representing the part + * of the byte array from offset for length + * bytes. + * + * @param buf the byte array + * @param offset offset in byte array to first byte to include + * @param length number of bytes to include + */ + public SharedByteArrayInputStream(byte[] buf, int offset, int length) { + super(buf, offset, length); + start = offset; + } + + /** + * Return the current position in the InputStream, as an + * offset from the beginning of the InputStream. + * + * @return the current position + */ + @Override + public long getPosition() { + return pos - start; + } + + /** + * Return a new InputStream representing a subset of the data + * from this InputStream, starting at start (inclusive) + * up to end (exclusive). start must be + * non-negative. If end is -1, the new stream ends + * at the same place as this stream. The returned InputStream + * will also implement the SharedInputStream interface. + * + * @param start the starting position + * @param end the ending position + 1 + * @return the new stream + */ + @Override + public InputStream newStream(long start, long end) { + if (start < 0) + throw new IllegalArgumentException("start < 0"); + if (end == -1) + end = count - this.start; + return new SharedByteArrayInputStream(buf, + this.start + (int)start, (int)(end - start)); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/util/SharedFileInputStream.java b/fine-third-default/fine-mail/src/javax/mail/util/SharedFileInputStream.java new file mode 100644 index 000000000..93ecc8080 --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/util/SharedFileInputStream.java @@ -0,0 +1,563 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package javax.mail.util; + +import java.io.*; +import javax.mail.internet.SharedInputStream; + +/** + * A SharedFileInputStream is a + * BufferedInputStream that buffers + * data from the file and supports the mark + * and reset methods. It also supports the + * newStream method that allows you to create + * other streams that represent subsets of the file. + * A RandomAccessFile object is used to + * access the file data.

    + * + * Note that when the SharedFileInputStream is closed, + * all streams created with the newStream + * method are also closed. This allows the creator of the + * SharedFileInputStream object to control access to the + * underlying file and ensure that it is closed when + * needed, to avoid leaking file descriptors. Note also + * that this behavior contradicts the requirements of + * SharedInputStream and may change in a future release. + * + * @author Bill Shannon + * @since JavaMail 1.4 + */ +public class SharedFileInputStream extends BufferedInputStream + implements SharedInputStream { + + private static int defaultBufferSize = 2048; + + /** + * The file containing the data. + * Shared by all related SharedFileInputStreams. + */ + protected RandomAccessFile in; + + /** + * The normal size of the read buffer. + */ + protected int bufsize; + + /** + * The file offset that corresponds to the first byte in + * the read buffer. + */ + protected long bufpos; + + /** + * The file offset of the start of data in this subset of the file. + */ + protected long start = 0; + + /** + * The amount of data in this subset of the file. + */ + protected long datalen; + + /** + * True if this is a top level stream created directly by "new". + * False if this is a derived stream created by newStream. + */ + private boolean master = true; + + /** + * A shared class that keeps track of the references + * to a particular file so it can be closed when the + * last reference is gone. + */ + static class SharedFile { + private int cnt; + private RandomAccessFile in; + + SharedFile(String file) throws IOException { + this.in = new RandomAccessFile(file, "r"); + } + + SharedFile(File file) throws IOException { + this.in = new RandomAccessFile(file, "r"); + } + + public synchronized RandomAccessFile open() { + cnt++; + return in; + } + + public synchronized void close() throws IOException { + if (cnt > 0 && --cnt <= 0) + in.close(); + } + + public synchronized void forceClose() throws IOException { + if (cnt > 0) { + // normal case, close exceptions propagated + cnt = 0; + in.close(); + } else { + // should already be closed, ignore exception + try { + in.close(); + } catch (IOException ioex) { } + } + } + + @Override + protected void finalize() throws Throwable { + try { + in.close(); + } finally { + super.finalize(); + } + } + } + + private SharedFile sf; + + /** + * Check to make sure that this stream has not been closed + */ + private void ensureOpen() throws IOException { + if (in == null) + throw new IOException("Stream closed"); + } + + /** + * Creates a SharedFileInputStream + * for the file. + * + * @param file the file + * @exception IOException for errors opening the file + */ + public SharedFileInputStream(File file) throws IOException { + this(file, defaultBufferSize); + } + + /** + * Creates a SharedFileInputStream + * for the named file + * + * @param file the file + * @exception IOException for errors opening the file + */ + public SharedFileInputStream(String file) throws IOException { + this(file, defaultBufferSize); + } + + /** + * Creates a SharedFileInputStream + * with the specified buffer size. + * + * @param file the file + * @param size the buffer size. + * @exception IOException for errors opening the file + * @exception IllegalArgumentException if size ≤ 0. + */ + public SharedFileInputStream(File file, int size) throws IOException { + super(null); // XXX - will it NPE? + if (size <= 0) + throw new IllegalArgumentException("Buffer size <= 0"); + init(new SharedFile(file), size); + } + + /** + * Creates a SharedFileInputStream + * with the specified buffer size. + * + * @param file the file + * @param size the buffer size. + * @exception IOException for errors opening the file + * @exception IllegalArgumentException if size ≤ 0. + */ + public SharedFileInputStream(String file, int size) throws IOException { + super(null); // XXX - will it NPE? + if (size <= 0) + throw new IllegalArgumentException("Buffer size <= 0"); + init(new SharedFile(file), size); + } + + private void init(SharedFile sf, int size) throws IOException { + this.sf = sf; + this.in = sf.open(); + this.start = 0; + this.datalen = in.length(); // XXX - file can't grow + this.bufsize = size; + buf = new byte[size]; + } + + /** + * Used internally by the newStream method. + */ + private SharedFileInputStream(SharedFile sf, long start, long len, + int bufsize) { + super(null); + this.master = false; + this.sf = sf; + this.in = sf.open(); + this.start = start; + this.bufpos = start; + this.datalen = len; + this.bufsize = bufsize; + buf = new byte[bufsize]; + } + + /** + * Fills the buffer with more data, taking into account + * shuffling and other tricks for dealing with marks. + * Assumes that it is being called by a synchronized method. + * This method also assumes that all data has already been read in, + * hence pos > count. + */ + private void fill() throws IOException { + if (markpos < 0) { + pos = 0; /* no mark: throw away the buffer */ + bufpos += count; + } else if (pos >= buf.length) /* no room left in buffer */ + if (markpos > 0) { /* can throw away early part of the buffer */ + int sz = pos - markpos; + System.arraycopy(buf, markpos, buf, 0, sz); + pos = sz; + bufpos += markpos; + markpos = 0; + } else if (buf.length >= marklimit) { + markpos = -1; /* buffer got too big, invalidate mark */ + pos = 0; /* drop buffer contents */ + bufpos += count; + } else { /* grow buffer */ + int nsz = pos * 2; + if (nsz > marklimit) + nsz = marklimit; + byte nbuf[] = new byte[nsz]; + System.arraycopy(buf, 0, nbuf, 0, pos); + buf = nbuf; + } + count = pos; + // limit to datalen + int len = buf.length - pos; + if (bufpos - start + pos + len > datalen) + len = (int)(datalen - (bufpos - start + pos)); + synchronized (in) { + in.seek(bufpos + pos); + int n = in.read(buf, pos, len); + if (n > 0) + count = n + pos; + } + } + + /** + * See the general contract of the read + * method of InputStream. + * + * @return the next byte of data, or -1 if the end of the + * stream is reached. + * @exception IOException if an I/O error occurs. + */ + @Override + public synchronized int read() throws IOException { + ensureOpen(); + if (pos >= count) { + fill(); + if (pos >= count) + return -1; + } + return buf[pos++] & 0xff; + } + + /** + * Read characters into a portion of an array, reading from the underlying + * stream at most once if necessary. + */ + private int read1(byte[] b, int off, int len) throws IOException { + int avail = count - pos; + if (avail <= 0) { + if (false) { + /* If the requested length is at least as large as the buffer, and + if there is no mark/reset activity, do not bother to copy the + bytes into the local buffer. In this way buffered streams will + cascade harmlessly. */ + if (len >= buf.length && markpos < 0) { + // XXX - seek, update bufpos - how? + return in.read(b, off, len); + } + } + fill(); + avail = count - pos; + if (avail <= 0) return -1; + } + int cnt = (avail < len) ? avail : len; + System.arraycopy(buf, pos, b, off, cnt); + pos += cnt; + return cnt; + } + + /** + * Reads bytes from this stream into the specified byte array, + * starting at the given offset. + * + *

    This method implements the general contract of the corresponding + * {@link java.io.InputStream#read(byte[], int, int) read} + * method of the {@link java.io.InputStream} class. + * + * @param b destination buffer. + * @param off offset at which to start storing bytes. + * @param len maximum number of bytes to read. + * @return the number of bytes read, or -1 if the end of + * the stream has been reached. + * @exception IOException if an I/O error occurs. + */ + @Override + public synchronized int read(byte b[], int off, int len) + throws IOException + { + ensureOpen(); + if ((off | len | (off + len) | (b.length - (off + len))) < 0) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + int n = read1(b, off, len); + if (n <= 0) return n; + while ((n < len) /* && (in.available() > 0) */) { + int n1 = read1(b, off + n, len - n); + if (n1 <= 0) break; + n += n1; + } + return n; + } + + /** + * See the general contract of the skip + * method of InputStream. + * + * @param n the number of bytes to be skipped. + * @return the actual number of bytes skipped. + * @exception IOException if an I/O error occurs. + */ + @Override + public synchronized long skip(long n) throws IOException { + ensureOpen(); + if (n <= 0) { + return 0; + } + long avail = count - pos; + + if (avail <= 0) { + // If no mark position set then don't keep in buffer + /* + if (markpos <0) + return in.skip(n); + */ + + // Fill in buffer to save bytes for reset + fill(); + avail = count - pos; + if (avail <= 0) + return 0; + } + + long skipped = (avail < n) ? avail : n; + pos += skipped; + return skipped; + } + + /** + * Returns the number of bytes that can be read from this input + * stream without blocking. + * + * @return the number of bytes that can be read from this input + * stream without blocking. + * @exception IOException if an I/O error occurs. + */ + @Override + public synchronized int available() throws IOException { + ensureOpen(); + return (count - pos) + in_available(); + } + + private int in_available() throws IOException { + // XXX - overflow + return (int)((start + datalen) - (bufpos + count)); + } + + /** + * See the general contract of the mark + * method of InputStream. + * + * @param readlimit the maximum limit of bytes that can be read before + * the mark position becomes invalid. + * @see #reset() + */ + @Override + public synchronized void mark(int readlimit) { + marklimit = readlimit; + markpos = pos; + } + + /** + * See the general contract of the reset + * method of InputStream. + *

    + * If markpos is -1 + * (no mark has been set or the mark has been + * invalidated), an IOException + * is thrown. Otherwise, pos is + * set equal to markpos. + * + * @exception IOException if this stream has not been marked or + * if the mark has been invalidated. + * @see #mark(int) + */ + @Override + public synchronized void reset() throws IOException { + ensureOpen(); + if (markpos < 0) + throw new IOException("Resetting to invalid mark"); + pos = markpos; + } + + /** + * Tests if this input stream supports the mark + * and reset methods. The markSupported + * method of SharedFileInputStream returns + * true. + * + * @return a boolean indicating if this stream type supports + * the mark and reset methods. + * @see java.io.InputStream#mark(int) + * @see java.io.InputStream#reset() + */ + @Override + public boolean markSupported() { + return true; + } + + /** + * Closes this input stream and releases any system resources + * associated with the stream. + * + * @exception IOException if an I/O error occurs. + */ + @Override + public void close() throws IOException { + if (in == null) + return; + try { + if (master) + sf.forceClose(); + else + sf.close(); + } finally { + sf = null; + in = null; + buf = null; + } + } + + /** + * Return the current position in the InputStream, as an + * offset from the beginning of the InputStream. + * + * @return the current position + */ + @Override + public long getPosition() { +//System.out.println("getPosition: start " + start + " pos " + pos +// + " bufpos " + bufpos + " = " + (bufpos + pos - start)); + if (in == null) + throw new RuntimeException("Stream closed"); + return bufpos + pos - start; + } + + /** + * Return a new InputStream representing a subset of the data + * from this InputStream, starting at start (inclusive) + * up to end (exclusive). start must be + * non-negative. If end is -1, the new stream ends + * at the same place as this stream. The returned InputStream + * will also implement the SharedInputStream interface. + * + * @param start the starting position + * @param end the ending position + 1 + * @return the new stream + */ + @Override + public synchronized InputStream newStream(long start, long end) { + if (in == null) + throw new RuntimeException("Stream closed"); + if (start < 0) + throw new IllegalArgumentException("start < 0"); + if (end == -1) + end = datalen; + return new SharedFileInputStream(sf, + this.start + start, end - start, bufsize); + } + + // for testing... + /* + public static void main(String[] argv) throws Exception { + SharedFileInputStream is = new SharedFileInputStream(argv[0]); + java.util.Random r = new java.util.Random(); + int b; + while ((b = is.read()) >= 0) { + System.out.write(b); + if (r.nextDouble() < 0.3) { + InputStream is2 = is.newStream(is.getPosition(), -1); + int b2; + while ((b2 = is2.read()) >= 0) + ; + } + } + } + */ + + /** + * Force this stream to close. + */ + @Override + protected void finalize() throws Throwable { + super.finalize(); + close(); + } +} diff --git a/fine-third-default/fine-mail/src/javax/mail/util/package.html b/fine-third-default/fine-mail/src/javax/mail/util/package.html new file mode 100644 index 000000000..069851d6c --- /dev/null +++ b/fine-third-default/fine-mail/src/javax/mail/util/package.html @@ -0,0 +1,58 @@ + + + + + + +javax.mail.util package + + + +

    +JavaMail API utility classes. +This package specifies utility classes that are useful with +other JavaMail APIs. +

    + + + diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/ILoggerFactory.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/ILoggerFactory.java new file mode 100644 index 000000000..8ed82f3ca --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/ILoggerFactory.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j; + +/** + * ILoggerFactory instances manufacture {@link Logger} + * instances by name. + * + *

    Most users retrieve {@link Logger} instances through the static + * {@link LoggerFactory#getLogger(String)} method. An instance of of this + * interface is bound internally with {@link LoggerFactory} class at + * compile time. + * + * @author Ceki Gülcü + */ +public interface ILoggerFactory { + + /** + * Return an appropriate {@link Logger} instance as specified by the + * name parameter. + * + *

    If the name parameter is equal to {@link Logger#ROOT_LOGGER_NAME}, that is + * the string value "ROOT" (case insensitive), then the root logger of the + * underlying logging system is returned. + * + *

    Null-valued name arguments are considered invalid. + * + *

    Certain extremely simple logging systems, e.g. NOP, may always + * return the same logger instance regardless of the requested name. + * + * @param name the name of the Logger to return + * @return a Logger instance + */ + public Logger getLogger(String name); +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/IMarkerFactory.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/IMarkerFactory.java new file mode 100644 index 000000000..757022150 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/IMarkerFactory.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j; + +/** + * Implementations of this interface are used to manufacture {@link Marker} + * instances. + * + *

    See the section Implementing + * the SLF4J API in the FAQ for details on how to make your logging + * system conform to SLF4J. + * + * @author Ceki Gülcü + */ +public interface IMarkerFactory { + + /** + * Manufacture a {@link Marker} instance by name. If the instance has been + * created earlier, return the previously created instance. + * + *

    Null name values are not allowed. + * + * @param name the name of the marker to be created, null value is + * not allowed. + * + * @return a Marker instance + */ + Marker getMarker(String name); + + /** + * Checks if the marker with the name already exists. If name is null, then false + * is returned. + * + * @param name logger name to check for + * @return true id the marker exists, false otherwise. + */ + boolean exists(String name); + + /** + * Detach an existing marker. + *

    + * Note that after a marker is detached, there might still be "dangling" references + * to the detached marker. + * + * + * @param name The name of the marker to detach + * @return whether the marker could be detached or not + */ + boolean detachMarker(String name); + + /** + * Create a marker which is detached (even at birth) from this IMarkerFactory. + * + * @param name marker name + * @return a dangling marker + * @since 1.5.1 + */ + Marker getDetachedMarker(String name); +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/Logger.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/Logger.java new file mode 100644 index 000000000..93dd9945e --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/Logger.java @@ -0,0 +1,721 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package org.slf4j; + +/** + * The org.slf4j.Logger interface is the main user entry point of SLF4J API. + * It is expected that logging takes place through concrete implementations + * of this interface. + *

    + *

    Typical usage pattern:

    + *
    + * import org.slf4j.Logger;
    + * import org.slf4j.LoggerFactory;
    + *
    + * public class Wombat {
    + *
    + *   final static Logger logger = LoggerFactory.getLogger(Wombat.class);
    + *   Integer t;
    + *   Integer oldT;
    + *
    + *   public void setTemperature(Integer temperature) {
    + *     oldT = t;
    + *     t = temperature;
    + *     logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);
    + *     if(temperature.intValue() > 50) {
    + *       logger.info("Temperature has risen above 50 degrees.");
    + *     }
    + *   }
    + * }
    + * 
    + * + * Be sure to read the FAQ entry relating to parameterized + * logging. Note that logging statements can be parameterized in + * presence of an exception/throwable. + * + *

    Once you are comfortable using loggers, i.e. instances of this interface, consider using + * MDC as well as Markers.

    + * + * @author Ceki Gülcü + */ +public interface Logger { + + /** + * Case insensitive String constant used to retrieve the name of the root logger. + * + * @since 1.3 + */ + final public String ROOT_LOGGER_NAME = "ROOT"; + + /** + * Return the name of this Logger instance. + * @return name of this logger instance + */ + public String getName(); + + /** + * Is the logger instance enabled for the TRACE level? + * + * @return True if this Logger is enabled for the TRACE level, + * false otherwise. + * @since 1.4 + */ + public boolean isTraceEnabled(); + + /** + * Log a message at the TRACE level. + * + * @param msg the message string to be logged + * @since 1.4 + */ + public void trace(String msg); + + /** + * Log a message at the TRACE level according to the specified format + * and argument. + *

    + *

    This form avoids superfluous object creation when the logger + * is disabled for the TRACE level.

    + * + * @param format the format string + * @param arg the argument + * @since 1.4 + */ + public void trace(String format, Object arg); + + /** + * Log a message at the TRACE level according to the specified format + * and arguments. + *

    + *

    This form avoids superfluous object creation when the logger + * is disabled for the TRACE level.

    + * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + * @since 1.4 + */ + public void trace(String format, Object arg1, Object arg2); + + /** + * Log a message at the TRACE level according to the specified format + * and arguments. + *

    + *

    This form avoids superfluous string concatenation when the logger + * is disabled for the TRACE level. However, this variant incurs the hidden + * (and relatively small) cost of creating an Object[] before invoking the method, + * even if this logger is disabled for TRACE. The variants taking {@link #trace(String, Object) one} and + * {@link #trace(String, Object, Object) two} arguments exist solely in order to avoid this hidden cost.

    + * + * @param format the format string + * @param arguments a list of 3 or more arguments + * @since 1.4 + */ + public void trace(String format, Object... arguments); + + /** + * Log an exception (throwable) at the TRACE level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + * @since 1.4 + */ + public void trace(String msg, Throwable t); + + /** + * Similar to {@link #isTraceEnabled()} method except that the + * marker data is also taken into account. + * + * @param marker The marker data to take into consideration + * @return True if this Logger is enabled for the TRACE level, + * false otherwise. + * + * @since 1.4 + */ + public boolean isTraceEnabled(Marker marker); + + /** + * Log a message with the specific Marker at the TRACE level. + * + * @param marker the marker data specific to this log statement + * @param msg the message string to be logged + * @since 1.4 + */ + public void trace(Marker marker, String msg); + + /** + * This method is similar to {@link #trace(String, Object)} method except that the + * marker data is also taken into consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg the argument + * @since 1.4 + */ + public void trace(Marker marker, String format, Object arg); + + /** + * This method is similar to {@link #trace(String, Object, Object)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + * @since 1.4 + */ + public void trace(Marker marker, String format, Object arg1, Object arg2); + + /** + * This method is similar to {@link #trace(String, Object...)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param argArray an array of arguments + * @since 1.4 + */ + public void trace(Marker marker, String format, Object... argArray); + + /** + * This method is similar to {@link #trace(String, Throwable)} method except that the + * marker data is also taken into consideration. + * + * @param marker the marker data specific to this log statement + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + * @since 1.4 + */ + public void trace(Marker marker, String msg, Throwable t); + + /** + * Is the logger instance enabled for the DEBUG level? + * + * @return True if this Logger is enabled for the DEBUG level, + * false otherwise. + */ + public boolean isDebugEnabled(); + + /** + * Log a message at the DEBUG level. + * + * @param msg the message string to be logged + */ + public void debug(String msg); + + /** + * Log a message at the DEBUG level according to the specified format + * and argument. + *

    + *

    This form avoids superfluous object creation when the logger + * is disabled for the DEBUG level.

    + * + * @param format the format string + * @param arg the argument + */ + public void debug(String format, Object arg); + + /** + * Log a message at the DEBUG level according to the specified format + * and arguments. + *

    + *

    This form avoids superfluous object creation when the logger + * is disabled for the DEBUG level.

    + * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void debug(String format, Object arg1, Object arg2); + + /** + * Log a message at the DEBUG level according to the specified format + * and arguments. + *

    + *

    This form avoids superfluous string concatenation when the logger + * is disabled for the DEBUG level. However, this variant incurs the hidden + * (and relatively small) cost of creating an Object[] before invoking the method, + * even if this logger is disabled for DEBUG. The variants taking + * {@link #debug(String, Object) one} and {@link #debug(String, Object, Object) two} + * arguments exist solely in order to avoid this hidden cost.

    + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + public void debug(String format, Object... arguments); + + /** + * Log an exception (throwable) at the DEBUG level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void debug(String msg, Throwable t); + + /** + * Similar to {@link #isDebugEnabled()} method except that the + * marker data is also taken into account. + * + * @param marker The marker data to take into consideration + * @return True if this Logger is enabled for the DEBUG level, + * false otherwise. + */ + public boolean isDebugEnabled(Marker marker); + + /** + * Log a message with the specific Marker at the DEBUG level. + * + * @param marker the marker data specific to this log statement + * @param msg the message string to be logged + */ + public void debug(Marker marker, String msg); + + /** + * This method is similar to {@link #debug(String, Object)} method except that the + * marker data is also taken into consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg the argument + */ + public void debug(Marker marker, String format, Object arg); + + /** + * This method is similar to {@link #debug(String, Object, Object)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void debug(Marker marker, String format, Object arg1, Object arg2); + + /** + * This method is similar to {@link #debug(String, Object...)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + public void debug(Marker marker, String format, Object... arguments); + + /** + * This method is similar to {@link #debug(String, Throwable)} method except that the + * marker data is also taken into consideration. + * + * @param marker the marker data specific to this log statement + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void debug(Marker marker, String msg, Throwable t); + + /** + * Is the logger instance enabled for the INFO level? + * + * @return True if this Logger is enabled for the INFO level, + * false otherwise. + */ + public boolean isInfoEnabled(); + + /** + * Log a message at the INFO level. + * + * @param msg the message string to be logged + */ + public void info(String msg); + + /** + * Log a message at the INFO level according to the specified format + * and argument. + *

    + *

    This form avoids superfluous object creation when the logger + * is disabled for the INFO level.

    + * + * @param format the format string + * @param arg the argument + */ + public void info(String format, Object arg); + + /** + * Log a message at the INFO level according to the specified format + * and arguments. + *

    + *

    This form avoids superfluous object creation when the logger + * is disabled for the INFO level.

    + * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void info(String format, Object arg1, Object arg2); + + /** + * Log a message at the INFO level according to the specified format + * and arguments. + *

    + *

    This form avoids superfluous string concatenation when the logger + * is disabled for the INFO level. However, this variant incurs the hidden + * (and relatively small) cost of creating an Object[] before invoking the method, + * even if this logger is disabled for INFO. The variants taking + * {@link #info(String, Object) one} and {@link #info(String, Object, Object) two} + * arguments exist solely in order to avoid this hidden cost.

    + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + public void info(String format, Object... arguments); + + /** + * Log an exception (throwable) at the INFO level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void info(String msg, Throwable t); + + /** + * Similar to {@link #isInfoEnabled()} method except that the marker + * data is also taken into consideration. + * + * @param marker The marker data to take into consideration + * @return true if this logger is warn enabled, false otherwise + */ + public boolean isInfoEnabled(Marker marker); + + /** + * Log a message with the specific Marker at the INFO level. + * + * @param marker The marker specific to this log statement + * @param msg the message string to be logged + */ + public void info(Marker marker, String msg); + + /** + * This method is similar to {@link #info(String, Object)} method except that the + * marker data is also taken into consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg the argument + */ + public void info(Marker marker, String format, Object arg); + + /** + * This method is similar to {@link #info(String, Object, Object)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void info(Marker marker, String format, Object arg1, Object arg2); + + /** + * This method is similar to {@link #info(String, Object...)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + public void info(Marker marker, String format, Object... arguments); + + /** + * This method is similar to {@link #info(String, Throwable)} method + * except that the marker data is also taken into consideration. + * + * @param marker the marker data for this log statement + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void info(Marker marker, String msg, Throwable t); + + /** + * Is the logger instance enabled for the WARN level? + * + * @return True if this Logger is enabled for the WARN level, + * false otherwise. + */ + public boolean isWarnEnabled(); + + /** + * Log a message at the WARN level. + * + * @param msg the message string to be logged + */ + public void warn(String msg); + + /** + * Log a message at the WARN level according to the specified format + * and argument. + *

    + *

    This form avoids superfluous object creation when the logger + * is disabled for the WARN level.

    + * + * @param format the format string + * @param arg the argument + */ + public void warn(String format, Object arg); + + /** + * Log a message at the WARN level according to the specified format + * and arguments. + *

    + *

    This form avoids superfluous string concatenation when the logger + * is disabled for the WARN level. However, this variant incurs the hidden + * (and relatively small) cost of creating an Object[] before invoking the method, + * even if this logger is disabled for WARN. The variants taking + * {@link #warn(String, Object) one} and {@link #warn(String, Object, Object) two} + * arguments exist solely in order to avoid this hidden cost.

    + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + public void warn(String format, Object... arguments); + + /** + * Log a message at the WARN level according to the specified format + * and arguments. + *

    + *

    This form avoids superfluous object creation when the logger + * is disabled for the WARN level.

    + * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void warn(String format, Object arg1, Object arg2); + + /** + * Log an exception (throwable) at the WARN level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void warn(String msg, Throwable t); + + /** + * Similar to {@link #isWarnEnabled()} method except that the marker + * data is also taken into consideration. + * + * @param marker The marker data to take into consideration + * @return True if this Logger is enabled for the WARN level, + * false otherwise. + */ + public boolean isWarnEnabled(Marker marker); + + /** + * Log a message with the specific Marker at the WARN level. + * + * @param marker The marker specific to this log statement + * @param msg the message string to be logged + */ + public void warn(Marker marker, String msg); + + /** + * This method is similar to {@link #warn(String, Object)} method except that the + * marker data is also taken into consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg the argument + */ + public void warn(Marker marker, String format, Object arg); + + /** + * This method is similar to {@link #warn(String, Object, Object)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void warn(Marker marker, String format, Object arg1, Object arg2); + + /** + * This method is similar to {@link #warn(String, Object...)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + public void warn(Marker marker, String format, Object... arguments); + + /** + * This method is similar to {@link #warn(String, Throwable)} method + * except that the marker data is also taken into consideration. + * + * @param marker the marker data for this log statement + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void warn(Marker marker, String msg, Throwable t); + + /** + * Is the logger instance enabled for the ERROR level? + * + * @return True if this Logger is enabled for the ERROR level, + * false otherwise. + */ + public boolean isErrorEnabled(); + + /** + * Log a message at the ERROR level. + * + * @param msg the message string to be logged + */ + public void error(String msg); + + /** + * Log a message at the ERROR level according to the specified format + * and argument. + *

    + *

    This form avoids superfluous object creation when the logger + * is disabled for the ERROR level.

    + * + * @param format the format string + * @param arg the argument + */ + public void error(String format, Object arg); + + /** + * Log a message at the ERROR level according to the specified format + * and arguments. + *

    + *

    This form avoids superfluous object creation when the logger + * is disabled for the ERROR level.

    + * + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void error(String format, Object arg1, Object arg2); + + /** + * Log a message at the ERROR level according to the specified format + * and arguments. + *

    + *

    This form avoids superfluous string concatenation when the logger + * is disabled for the ERROR level. However, this variant incurs the hidden + * (and relatively small) cost of creating an Object[] before invoking the method, + * even if this logger is disabled for ERROR. The variants taking + * {@link #error(String, Object) one} and {@link #error(String, Object, Object) two} + * arguments exist solely in order to avoid this hidden cost.

    + * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + public void error(String format, Object... arguments); + + /** + * Log an exception (throwable) at the ERROR level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void error(String msg, Throwable t); + + /** + * Similar to {@link #isErrorEnabled()} method except that the + * marker data is also taken into consideration. + * + * @param marker The marker data to take into consideration + * @return True if this Logger is enabled for the ERROR level, + * false otherwise. + */ + public boolean isErrorEnabled(Marker marker); + + /** + * Log a message with the specific Marker at the ERROR level. + * + * @param marker The marker specific to this log statement + * @param msg the message string to be logged + */ + public void error(Marker marker, String msg); + + /** + * This method is similar to {@link #error(String, Object)} method except that the + * marker data is also taken into consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg the argument + */ + public void error(Marker marker, String format, Object arg); + + /** + * This method is similar to {@link #error(String, Object, Object)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arg1 the first argument + * @param arg2 the second argument + */ + public void error(Marker marker, String format, Object arg1, Object arg2); + + /** + * This method is similar to {@link #error(String, Object...)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + public void error(Marker marker, String format, Object... arguments); + + /** + * This method is similar to {@link #error(String, Throwable)} + * method except that the marker data is also taken into + * consideration. + * + * @param marker the marker data specific to this log statement + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + public void error(Marker marker, String msg, Throwable t); + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/LoggerFactory.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/LoggerFactory.java new file mode 100644 index 000000000..30b5c7257 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/LoggerFactory.java @@ -0,0 +1,430 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.LinkedBlockingQueue; + +import org.slf4j.event.SubstituteLoggingEvent; +import org.slf4j.helpers.NOPLoggerFactory; +import org.slf4j.helpers.SubstituteLogger; +import org.slf4j.helpers.SubstituteLoggerFactory; +import org.slf4j.helpers.Util; +import org.slf4j.impl.StaticLoggerBinder; + +/** + * The LoggerFactory is a utility class producing Loggers for + * various logging APIs, most notably for log4j, logback and JDK 1.4 logging. + * Other implementations such as {@link org.slf4j.impl.NOPLogger NOPLogger} and + * {@link org.slf4j.impl.SimpleLogger SimpleLogger} are also supported. + *

    + *

    + * LoggerFactory is essentially a wrapper around an + * {@link ILoggerFactory} instance bound with LoggerFactory at + * compile time. + *

    + *

    + * Please note that all methods in LoggerFactory are static. + * + * + * @author Alexander Dorokhine + * @author Robert Elliot + * @author Ceki Gülcü + * + */ +public final class LoggerFactory { + + static final String CODES_PREFIX = "http://www.slf4j.org/codes.html"; + + static final String NO_STATICLOGGERBINDER_URL = CODES_PREFIX + "#StaticLoggerBinder"; + static final String MULTIPLE_BINDINGS_URL = CODES_PREFIX + "#multiple_bindings"; + static final String NULL_LF_URL = CODES_PREFIX + "#null_LF"; + static final String VERSION_MISMATCH = CODES_PREFIX + "#version_mismatch"; + static final String SUBSTITUTE_LOGGER_URL = CODES_PREFIX + "#substituteLogger"; + static final String LOGGER_NAME_MISMATCH_URL = CODES_PREFIX + "#loggerNameMismatch"; + static final String REPLAY_URL = CODES_PREFIX + "#replay"; + + static final String UNSUCCESSFUL_INIT_URL = CODES_PREFIX + "#unsuccessfulInit"; + static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also " + UNSUCCESSFUL_INIT_URL; + + static final int UNINITIALIZED = 0; + static final int ONGOING_INITIALIZATION = 1; + static final int FAILED_INITIALIZATION = 2; + static final int SUCCESSFUL_INITIALIZATION = 3; + static final int NOP_FALLBACK_INITIALIZATION = 4; + + static volatile int INITIALIZATION_STATE = UNINITIALIZED; + static final SubstituteLoggerFactory SUBST_FACTORY = new SubstituteLoggerFactory(); + static final NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory(); + + // Support for detecting mismatched logger names. + static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch"; + static final String JAVA_VENDOR_PROPERTY = "java.vendor.url"; + + static boolean DETECT_LOGGER_NAME_MISMATCH = Util.safeGetBooleanSystemProperty(DETECT_LOGGER_NAME_MISMATCH_PROPERTY); + + /** + * It is LoggerFactory's responsibility to track version changes and manage + * the compatibility list. + *

    + *

    + * It is assumed that all versions in the 1.6 are mutually compatible. + */ + static private final String[] API_COMPATIBILITY_LIST = new String[] { "1.6", "1.7" }; + + // private constructor prevents instantiation + private LoggerFactory() { + } + + /** + * Force LoggerFactory to consider itself uninitialized. + *

    + *

    + * This method is intended to be called by classes (in the same package) for + * testing purposes. This method is internal. It can be modified, renamed or + * removed at any time without notice. + *

    + *

    + * You are strongly discouraged from calling this method in production code. + */ + static void reset() { + INITIALIZATION_STATE = UNINITIALIZED; + } + + private final static void performInitialization() { + bind(); + if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) { + versionSanityCheck(); + } + } + + private static boolean messageContainsOrgSlf4jImplStaticLoggerBinder(String msg) { + if (msg == null) + return false; + if (msg.contains("org/slf4j/impl/StaticLoggerBinder")) + return true; + if (msg.contains("org.slf4j.impl.StaticLoggerBinder")) + return true; + return false; + } + + private final static void bind() { + try { + Set staticLoggerBinderPathSet = null; + // skip check under android, see also + // http://jira.qos.ch/browse/SLF4J-328 + if (!isAndroid()) { + staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); + reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); + } + // the next line does the binding + StaticLoggerBinder.getSingleton(); + INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION; + reportActualBinding(staticLoggerBinderPathSet); + fixSubstituteLoggers(); + replayEvents(); + // release all resources in SUBST_FACTORY + SUBST_FACTORY.clear(); + } catch (NoClassDefFoundError ncde) { + String msg = ncde.getMessage(); + if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) { + INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION; + Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\"."); + Util.report("Defaulting to no-operation (NOP) logger implementation"); + Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details."); + } else { + failedBinding(ncde); + throw ncde; + } + } catch (java.lang.NoSuchMethodError nsme) { + String msg = nsme.getMessage(); + if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) { + INITIALIZATION_STATE = FAILED_INITIALIZATION; + Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding."); + Util.report("Your binding is version 1.5.5 or earlier."); + Util.report("Upgrade your binding to version 1.6.x."); + } + throw nsme; + } catch (Exception e) { + failedBinding(e); + throw new IllegalStateException("Unexpected initialization failure", e); + } + } + + private static void fixSubstituteLoggers() { + synchronized (SUBST_FACTORY) { + SUBST_FACTORY.postInitialization(); + for (SubstituteLogger substLogger : SUBST_FACTORY.getLoggers()) { + Logger logger = getLogger(substLogger.getName()); + substLogger.setDelegate(logger); + } + } + + } + + static void failedBinding(Throwable t) { + INITIALIZATION_STATE = FAILED_INITIALIZATION; + Util.report("Failed to instantiate SLF4J LoggerFactory", t); + } + + private static void replayEvents() { + final LinkedBlockingQueue queue = SUBST_FACTORY.getEventQueue(); + final int queueSize = queue.size(); + int count = 0; + final int maxDrain = 128; + List eventList = new ArrayList(maxDrain); + while (true) { + int numDrained = queue.drainTo(eventList, maxDrain); + if (numDrained == 0) + break; + for (SubstituteLoggingEvent event : eventList) { + replaySingleEvent(event); + if (count++ == 0) + emitReplayOrSubstituionWarning(event, queueSize); + } + eventList.clear(); + } + } + + private static void emitReplayOrSubstituionWarning(SubstituteLoggingEvent event, int queueSize) { + if (event.getLogger().isDelegateEventAware()) { + emitReplayWarning(queueSize); + } else if (event.getLogger().isDelegateNOP()) { + // nothing to do + } else { + emitSubstitutionWarning(); + } + } + + private static void replaySingleEvent(SubstituteLoggingEvent event) { + if (event == null) + return; + + SubstituteLogger substLogger = event.getLogger(); + String loggerName = substLogger.getName(); + if (substLogger.isDelegateNull()) { + throw new IllegalStateException("Delegate logger cannot be null at this state."); + } + + if (substLogger.isDelegateNOP()) { + // nothing to do + } else if (substLogger.isDelegateEventAware()) { + substLogger.log(event); + } else { + Util.report(loggerName); + } + } + + private static void emitSubstitutionWarning() { + Util.report("The following set of substitute loggers may have been accessed"); + Util.report("during the initialization phase. Logging calls during this"); + Util.report("phase were not honored. However, subsequent logging calls to these"); + Util.report("loggers will work as normally expected."); + Util.report("See also " + SUBSTITUTE_LOGGER_URL); + } + + private static void emitReplayWarning(int eventCount) { + Util.report("A number (" + eventCount + ") of logging calls during the initialization phase have been intercepted and are"); + Util.report("now being replayed. These are subject to the filtering rules of the underlying logging system."); + Util.report("See also " + REPLAY_URL); + } + + private final static void versionSanityCheck() { + try { + String requested = StaticLoggerBinder.REQUESTED_API_VERSION; + + boolean match = false; + for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) { + if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) { + match = true; + } + } + if (!match) { + Util.report("The requested version " + requested + " by your slf4j binding is not compatible with " + + Arrays.asList(API_COMPATIBILITY_LIST).toString()); + Util.report("See " + VERSION_MISMATCH + " for further details."); + } + } catch (java.lang.NoSuchFieldError nsfe) { + // given our large user base and SLF4J's commitment to backward + // compatibility, we cannot cry here. Only for implementations + // which willingly declare a REQUESTED_API_VERSION field do we + // emit compatibility warnings. + } catch (Throwable e) { + // we should never reach here + Util.report("Unexpected problem occured during version sanity check", e); + } + } + + // We need to use the name of the StaticLoggerBinder class, but we can't + // reference + // the class itself. + private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; + + static Set findPossibleStaticLoggerBinderPathSet() { + // use Set instead of list in order to deal with bug #138 + // LinkedHashSet appropriate here because it preserves insertion order + // during iteration + Set staticLoggerBinderPathSet = new LinkedHashSet(); + try { + ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader(); + Enumeration paths; + if (loggerFactoryClassLoader == null) { + paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); + } else { + paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH); + } + while (paths.hasMoreElements()) { + URL path = paths.nextElement(); + staticLoggerBinderPathSet.add(path); + } + } catch (IOException ioe) { + Util.report("Error getting resources from path", ioe); + } + return staticLoggerBinderPathSet; + } + + private static boolean isAmbiguousStaticLoggerBinderPathSet(Set binderPathSet) { + return binderPathSet.size() > 1; + } + + /** + * Prints a warning message on the console if multiple bindings were found + * on the class path. No reporting is done otherwise. + * + */ + private static void reportMultipleBindingAmbiguity(Set binderPathSet) { + if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) { + Util.report("Class path contains multiple SLF4J bindings."); + for (URL path : binderPathSet) { + Util.report("Found binding in [" + path + "]"); + } + Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation."); + } + } + + private static boolean isAndroid() { + String vendor = Util.safeGetSystemProperty(JAVA_VENDOR_PROPERTY); + if (vendor == null) + return false; + return vendor.toLowerCase().contains("android"); + } + + private static void reportActualBinding(Set binderPathSet) { + // binderPathSet can be null under Android + if (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) { + Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]"); + } + } + + /** + * Return a logger named according to the name parameter using the + * statically bound {@link ILoggerFactory} instance. + * + * @param name + * The name of the logger. + * @return logger + */ + public static Logger getLogger(String name) { + ILoggerFactory iLoggerFactory = getILoggerFactory(); + return iLoggerFactory.getLogger(name); + } + + /** + * Return a logger named corresponding to the class passed as parameter, + * using the statically bound {@link ILoggerFactory} instance. + * + *

    + * In case the the clazz parameter differs from the name of the + * caller as computed internally by SLF4J, a logger name mismatch warning + * will be printed but only if the + * slf4j.detectLoggerNameMismatch system property is set to + * true. By default, this property is not set and no warnings will be + * printed even in case of a logger name mismatch. + * + * @param clazz + * the returned logger will be named after clazz + * @return logger + * + * + * @see Detected + * logger name mismatch + */ + public static Logger getLogger(Class clazz) { + Logger logger = getLogger(clazz.getName()); + if (DETECT_LOGGER_NAME_MISMATCH) { + Class autoComputedCallingClass = Util.getCallingClass(); + if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) { + Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), + autoComputedCallingClass.getName())); + Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation"); + } + } + return logger; + } + + private static boolean nonMatchingClasses(Class clazz, Class autoComputedCallingClass) { + return !autoComputedCallingClass.isAssignableFrom(clazz); + } + + /** + * Return the {@link ILoggerFactory} instance in use. + *

    + *

    + * ILoggerFactory instance is bound with this class at compile time. + * + * @return the ILoggerFactory instance in use + */ + public static ILoggerFactory getILoggerFactory() { + if (INITIALIZATION_STATE == UNINITIALIZED) { + synchronized (LoggerFactory.class) { + if (INITIALIZATION_STATE == UNINITIALIZED) { + INITIALIZATION_STATE = ONGOING_INITIALIZATION; + performInitialization(); + } + } + } + switch (INITIALIZATION_STATE) { + case SUCCESSFUL_INITIALIZATION: + return StaticLoggerBinder.getSingleton().getLoggerFactory(); + case NOP_FALLBACK_INITIALIZATION: + return NOP_FALLBACK_FACTORY; + case FAILED_INITIALIZATION: + throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG); + case ONGOING_INITIALIZATION: + // support re-entrant behavior. + // See also http://jira.qos.ch/browse/SLF4J-97 + return SUBST_FACTORY; + } + throw new IllegalStateException("Unreachable code"); + } +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/MDC.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/MDC.java new file mode 100644 index 000000000..71d761d49 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/MDC.java @@ -0,0 +1,277 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j; + +import java.io.Closeable; +import java.util.Map; + +import org.slf4j.helpers.NOPMDCAdapter; +import org.slf4j.helpers.BasicMDCAdapter; +import org.slf4j.helpers.Util; +import org.slf4j.impl.StaticMDCBinder; +import org.slf4j.spi.MDCAdapter; + +/** + * This class hides and serves as a substitute for the underlying logging + * system's MDC implementation. + * + *

    + * If the underlying logging system offers MDC functionality, then SLF4J's MDC, + * i.e. this class, will delegate to the underlying system's MDC. Note that at + * this time, only two logging systems, namely log4j and logback, offer MDC + * functionality. For java.util.logging which does not support MDC, + * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple + * and slf4j-nop, {@link NOPMDCAdapter} will be used. + * + *

    + * Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j, + * logback, or java.util.logging, but without forcing these systems as + * dependencies upon your users. + * + *

    + * For more information on MDC please see the chapter on MDC in the + * logback manual. + * + *

    + * Please note that all methods in this class are static. + * + * @author Ceki Gülcü + * @since 1.4.1 + */ +public class MDC { + + static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA"; + static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder"; + static MDCAdapter mdcAdapter; + + /** + * An adapter to remove the key when done. + */ + public static class MDCCloseable implements Closeable { + private final String key; + + private MDCCloseable(String key) { + this.key = key; + } + + public void close() { + MDC.remove(this.key); + } + } + + private MDC() { + } + + /** + * As of SLF4J version 1.7.14, StaticMDCBinder classes shipping in various bindings + * come with a getSingleton() method. Previously only a public field called SINGLETON + * was available. + * + * @return MDCAdapter + * @throws NoClassDefFoundError in case no binding is available + * @since 1.7.14 + */ + private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError { + try { + return StaticMDCBinder.getSingleton().getMDCA(); + } catch (NoSuchMethodError nsme) { + // binding is probably a version of SLF4J older than 1.7.14 + return StaticMDCBinder.SINGLETON.getMDCA(); + } + } + + static { + try { + mdcAdapter = bwCompatibleGetMDCAdapterFromBinder(); + } catch (NoClassDefFoundError ncde) { + mdcAdapter = new NOPMDCAdapter(); + String msg = ncde.getMessage(); + if (msg != null && msg.contains("StaticMDCBinder")) { + Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\"."); + Util.report("Defaulting to no-operation MDCAdapter implementation."); + Util.report("See " + NO_STATIC_MDC_BINDER_URL + " for further details."); + } else { + throw ncde; + } + } catch (Exception e) { + // we should never get here + Util.report("MDC binding unsuccessful.", e); + } + } + + /** + * Put a diagnostic context value (the val parameter) as identified with the + * key parameter into the current thread's diagnostic context map. The + * key parameter cannot be null. The val parameter + * can be null only if the underlying implementation supports it. + * + *

    + * This method delegates all work to the MDC of the underlying logging system. + * + * @param key non-null key + * @param val value to put in the map + * + * @throws IllegalArgumentException + * in case the "key" parameter is null + */ + public static void put(String key, String val) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException("key parameter cannot be null"); + } + if (mdcAdapter == null) { + throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); + } + mdcAdapter.put(key, val); + } + + /** + * Put a diagnostic context value (the val parameter) as identified with the + * key parameter into the current thread's diagnostic context map. The + * key parameter cannot be null. The val parameter + * can be null only if the underlying implementation supports it. + * + *

    + * This method delegates all work to the MDC of the underlying logging system. + *

    + * This method return a Closeable object who can remove key when + * close is called. + * + *

    + * Useful with Java 7 for example : + * + * try(MDC.MDCCloseable closeable = MDC.putCloseable(key, value)) { + * .... + * } + * + * + * @param key non-null key + * @param val value to put in the map + * @return a Closeable who can remove key when close + * is called. + * + * @throws IllegalArgumentException + * in case the "key" parameter is null + */ + public static MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException { + put(key, val); + return new MDCCloseable(key); + } + + /** + * Get the diagnostic context identified by the key parameter. The + * key parameter cannot be null. + * + *

    + * This method delegates all work to the MDC of the underlying logging system. + * + * @param key + * @return the string value identified by the key parameter. + * @throws IllegalArgumentException + * in case the "key" parameter is null + */ + public static String get(String key) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException("key parameter cannot be null"); + } + + if (mdcAdapter == null) { + throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); + } + return mdcAdapter.get(key); + } + + /** + * Remove the diagnostic context identified by the key parameter using + * the underlying system's MDC implementation. The key parameter + * cannot be null. This method does nothing if there is no previous value + * associated with key. + * + * @param key + * @throws IllegalArgumentException + * in case the "key" parameter is null + */ + public static void remove(String key) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException("key parameter cannot be null"); + } + + if (mdcAdapter == null) { + throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); + } + mdcAdapter.remove(key); + } + + /** + * Clear all entries in the MDC of the underlying implementation. + */ + public static void clear() { + if (mdcAdapter == null) { + throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); + } + mdcAdapter.clear(); + } + + /** + * Return a copy of the current thread's context map, with keys and values of + * type String. Returned value may be null. + * + * @return A copy of the current thread's context map. May be null. + * @since 1.5.1 + */ + public static Map getCopyOfContextMap() { + if (mdcAdapter == null) { + throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); + } + return mdcAdapter.getCopyOfContextMap(); + } + + /** + * Set the current thread's context map by first clearing any existing map and + * then copying the map passed as parameter. The context map passed as + * parameter must only contain keys and values of type String. + * + * @param contextMap + * must contain only keys and values of type String + * @since 1.5.1 + */ + public static void setContextMap(Map contextMap) { + if (mdcAdapter == null) { + throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); + } + mdcAdapter.setContextMap(contextMap); + } + + /** + * Returns the MDCAdapter instance currently in use. + * + * @return the MDcAdapter instance currently in use. + * @since 1.4.2 + */ + public static MDCAdapter getMDCAdapter() { + return mdcAdapter; + } + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/Marker.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/Marker.java new file mode 100644 index 000000000..c6bfd5e27 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/Marker.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j; + +import java.io.Serializable; +import java.util.Iterator; + +/** + * Markers are named objects used to enrich log statements. Conforming logging + * system Implementations of SLF4J determine how information conveyed by markers + * are used, if at all. In particular, many conforming logging systems ignore + * marker data. + * + *

    + * Markers can contain references to other markers, which in turn may contain + * references of their own. + * + * @author Ceki Gülcü + */ +public interface Marker extends Serializable { + + /** + * This constant represents any marker, including a null marker. + */ + public final String ANY_MARKER = "*"; + + /** + * This constant represents any non-null marker. + */ + public final String ANY_NON_NULL_MARKER = "+"; + + /** + * Get the name of this Marker. + * + * @return name of marker + */ + public String getName(); + + /** + * Add a reference to another Marker. + * + * @param reference + * a reference to another marker + * @throws IllegalArgumentException + * if 'reference' is null + */ + public void add(Marker reference); + + /** + * Remove a marker reference. + * + * @param reference + * the marker reference to remove + * @return true if reference could be found and removed, false otherwise. + */ + public boolean remove(Marker reference); + + /** + * @deprecated Replaced by {@link #hasReferences()}. + */ + public boolean hasChildren(); + + /** + * Does this marker have any references? + * + * @return true if this marker has one or more references, false otherwise. + */ + public boolean hasReferences(); + + /** + * Returns an Iterator which can be used to iterate over the references of this + * marker. An empty iterator is returned when this marker has no references. + * + * @return Iterator over the references of this marker + */ + public Iterator iterator(); + + /** + * Does this marker contain a reference to the 'other' marker? Marker A is defined + * to contain marker B, if A == B or if B is referenced by A, or if B is referenced + * by any one of A's references (recursively). + * + * @param other + * The marker to test for inclusion. + * @throws IllegalArgumentException + * if 'other' is null + * @return Whether this marker contains the other marker. + */ + public boolean contains(Marker other); + + /** + * Does this marker contain the marker named 'name'? + * + * If 'name' is null the returned value is always false. + * + * @param name The marker name to test for inclusion. + * @return Whether this marker contains the other marker. + */ + public boolean contains(String name); + + /** + * Markers are considered equal if they have the same name. + * + * @param o + * @return true, if this.name equals o.name + * + * @since 1.5.1 + */ + public boolean equals(Object o); + + /** + * Compute the hash code based on the name of this marker. + * Note that markers are considered equal if they have the same name. + * + * @return the computed hashCode + * @since 1.5.1 + */ + public int hashCode(); + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/MarkerFactory.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/MarkerFactory.java new file mode 100644 index 000000000..7eff41f6a --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/MarkerFactory.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j; + +import org.slf4j.helpers.BasicMarkerFactory; +import org.slf4j.helpers.Util; +import org.slf4j.impl.StaticMarkerBinder; + +/** + * MarkerFactory is a utility class producing {@link Marker} instances as + * appropriate for the logging system currently in use. + * + *

    + * This class is essentially implemented as a wrapper around an + * {@link IMarkerFactory} instance bound at compile time. + * + *

    + * Please note that all methods in this class are static. + * + * @author Ceki Gülcü + */ +public class MarkerFactory { + static IMarkerFactory MARKER_FACTORY; + + private MarkerFactory() { + } + + /** + * As of SLF4J version 1.7.14, StaticMarkerBinder classes shipping in various bindings + * come with a getSingleton() method. Previously only a public field called SINGLETON + * was available. + * + * @return IMarkerFactory + * @throws NoClassDefFoundError in case no binding is available + * @since 1.7.14 + */ + private static IMarkerFactory bwCompatibleGetMarkerFactoryFromBinder() throws NoClassDefFoundError { + try { + return StaticMarkerBinder.getSingleton().getMarkerFactory(); + } catch (NoSuchMethodError nsme) { + // binding is probably a version of SLF4J older than 1.7.14 + return StaticMarkerBinder.SINGLETON.getMarkerFactory(); + } + } + + // this is where the binding happens + static { + try { + MARKER_FACTORY = bwCompatibleGetMarkerFactoryFromBinder(); + } catch (NoClassDefFoundError e) { + MARKER_FACTORY = new BasicMarkerFactory(); + } catch (Exception e) { + // we should never get here + Util.report("Unexpected failure while binding MarkerFactory", e); + } + } + + /** + * Return a Marker instance as specified by the name parameter using the + * previously bound {@link IMarkerFactory}instance. + * + * @param name + * The name of the {@link Marker} object to return. + * @return marker + */ + public static Marker getMarker(String name) { + return MARKER_FACTORY.getMarker(name); + } + + /** + * Create a marker which is detached (even at birth) from the MarkerFactory. + * + * @param name the name of the marker + * @return a dangling marker + * @since 1.5.1 + */ + public static Marker getDetachedMarker(String name) { + return MARKER_FACTORY.getDetachedMarker(name); + } + + /** + * Return the {@link IMarkerFactory}instance in use. + * + *

    The IMarkerFactory instance is usually bound with this class at + * compile time. + * + * @return the IMarkerFactory instance in use + */ + public static IMarkerFactory getIMarkerFactory() { + return MARKER_FACTORY; + } +} \ No newline at end of file diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/event/EventConstants.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/event/EventConstants.java new file mode 100644 index 000000000..00975da27 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/event/EventConstants.java @@ -0,0 +1,13 @@ +package org.slf4j.event; + +import org.slf4j.spi.LocationAwareLogger; + +public class EventConstants { + public static final int ERROR_INT = LocationAwareLogger.ERROR_INT; + public static final int WARN_INT = LocationAwareLogger.WARN_INT; + public static final int INFO_INT = LocationAwareLogger.INFO_INT; + public static final int DEBUG_INT = LocationAwareLogger.DEBUG_INT; + public static final int TRACE_INT = LocationAwareLogger.TRACE_INT; + public static final String NA_SUBST = "NA/SubstituteLogger"; + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/event/EventRecodingLogger.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/event/EventRecodingLogger.java new file mode 100644 index 000000000..978d12de6 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/event/EventRecodingLogger.java @@ -0,0 +1,323 @@ +package org.slf4j.event; + +import java.util.Queue; + +import org.slf4j.Logger; +import org.slf4j.Marker; +import org.slf4j.helpers.MessageFormatter; +import org.slf4j.helpers.SubstituteLogger; + +/** + * + * This class is used to record events during the initialization phase of the + * underlying logging framework. It is called by {@link SubstituteLogger}. + * + * + * @author Ceki Gülcü + * @author Wessel van Norel + * + */ +public class EventRecodingLogger implements Logger { + + String name; + SubstituteLogger logger; + Queue eventQueue; + + // as an event recording logger we have no choice but to record all events + final static boolean RECORD_ALL_EVENTS = true; + + public EventRecodingLogger(SubstituteLogger logger, Queue eventQueue) { + this.logger = logger; + this.name = logger.getName(); + this.eventQueue = eventQueue; + } + + public String getName() { + return name; + } + + public boolean isTraceEnabled() { + return RECORD_ALL_EVENTS; + } + + public void trace(String msg) { + recordEvent_0Args(Level.TRACE, null, msg, null); + } + + public void trace(String format, Object arg) { + recordEvent_1Args(Level.TRACE, null, format, arg); + } + + public void trace(String format, Object arg1, Object arg2) { + recordEvent2Args(Level.TRACE, null, format, arg1, arg2); + } + + public void trace(String format, Object... arguments) { + recordEventArgArray(Level.TRACE, null, format, arguments); + } + + public void trace(String msg, Throwable t) { + recordEvent_0Args(Level.TRACE, null, msg, t); + } + + public boolean isTraceEnabled(Marker marker) { + return RECORD_ALL_EVENTS; + } + + public void trace(Marker marker, String msg) { + recordEvent_0Args(Level.TRACE, marker, msg, null); + } + + public void trace(Marker marker, String format, Object arg) { + recordEvent_1Args(Level.TRACE, marker, format, arg); + } + + public void trace(Marker marker, String format, Object arg1, Object arg2) { + recordEvent2Args(Level.TRACE, marker, format, arg1, arg2); + } + + public void trace(Marker marker, String format, Object... argArray) { + recordEventArgArray(Level.TRACE, marker, format, argArray); + } + + public void trace(Marker marker, String msg, Throwable t) { + recordEvent_0Args(Level.TRACE, marker, msg, t); + } + + public boolean isDebugEnabled() { + return RECORD_ALL_EVENTS; + } + + public void debug(String msg) { + recordEvent_0Args(Level.DEBUG, null, msg, null); + } + + public void debug(String format, Object arg) { + recordEvent_1Args(Level.DEBUG, null, format, arg); + } + + public void debug(String format, Object arg1, Object arg2) { + recordEvent2Args(Level.DEBUG, null, format, arg1, arg2); + } + + public void debug(String format, Object... arguments) { + recordEventArgArray(Level.DEBUG, null, format, arguments); + } + + public void debug(String msg, Throwable t) { + recordEvent_0Args(Level.DEBUG, null, msg, t); + } + + public boolean isDebugEnabled(Marker marker) { + return RECORD_ALL_EVENTS; + } + + public void debug(Marker marker, String msg) { + recordEvent_0Args(Level.DEBUG, marker, msg, null); + } + + public void debug(Marker marker, String format, Object arg) { + recordEvent_1Args(Level.DEBUG, marker, format, arg); + } + + public void debug(Marker marker, String format, Object arg1, Object arg2) { + recordEvent2Args(Level.DEBUG, marker, format, arg1, arg2); + } + + public void debug(Marker marker, String format, Object... arguments) { + recordEventArgArray(Level.DEBUG, marker, format, arguments); + } + + public void debug(Marker marker, String msg, Throwable t) { + recordEvent_0Args(Level.DEBUG, marker, msg, t); + } + + public boolean isInfoEnabled() { + return RECORD_ALL_EVENTS; + } + + public void info(String msg) { + recordEvent_0Args(Level.INFO, null, msg, null); + } + + public void info(String format, Object arg) { + recordEvent_1Args(Level.INFO, null, format, arg); + } + + public void info(String format, Object arg1, Object arg2) { + recordEvent2Args(Level.INFO, null, format, arg1, arg2); + } + + public void info(String format, Object... arguments) { + recordEventArgArray(Level.INFO, null, format, arguments); + } + + public void info(String msg, Throwable t) { + recordEvent_0Args(Level.INFO, null, msg, t); + } + + public boolean isInfoEnabled(Marker marker) { + return RECORD_ALL_EVENTS; + } + + public void info(Marker marker, String msg) { + recordEvent_0Args(Level.INFO, marker, msg, null); + } + + public void info(Marker marker, String format, Object arg) { + recordEvent_1Args(Level.INFO, marker, format, arg); + } + + public void info(Marker marker, String format, Object arg1, Object arg2) { + recordEvent2Args(Level.INFO, marker, format, arg1, arg2); + } + + public void info(Marker marker, String format, Object... arguments) { + recordEventArgArray(Level.INFO, marker, format, arguments); + } + + public void info(Marker marker, String msg, Throwable t) { + recordEvent_0Args(Level.INFO, marker, msg, t); + + } + + public boolean isWarnEnabled() { + return RECORD_ALL_EVENTS; + } + + public void warn(String msg) { + recordEvent_0Args(Level.WARN, null, msg, null); + } + + public void warn(String format, Object arg) { + recordEvent_1Args(Level.WARN, null, format, arg); + } + + public void warn(String format, Object arg1, Object arg2) { + recordEvent2Args(Level.WARN, null, format, arg1, arg2); + } + + public void warn(String format, Object... arguments) { + recordEventArgArray(Level.WARN, null, format, arguments); + } + + public void warn(String msg, Throwable t) { + recordEvent_0Args(Level.WARN, null, msg, t); + } + + public boolean isWarnEnabled(Marker marker) { + return RECORD_ALL_EVENTS; + } + + public void warn(Marker marker, String msg) { + recordEvent_0Args(Level.WARN, marker, msg, null); + } + + public void warn(Marker marker, String format, Object arg) { + recordEvent_1Args(Level.WARN, marker, format, arg); + } + + public void warn(Marker marker, String format, Object arg1, Object arg2) { + recordEvent2Args(Level.WARN, marker, format, arg1, arg2); + } + + public void warn(Marker marker, String format, Object... arguments) { + recordEventArgArray(Level.WARN, marker, format, arguments); + } + + public void warn(Marker marker, String msg, Throwable t) { + recordEvent_0Args(Level.WARN, marker, msg, t); + } + + public boolean isErrorEnabled() { + return RECORD_ALL_EVENTS; + } + + public void error(String msg) { + recordEvent_0Args(Level.ERROR, null, msg, null); + } + + public void error(String format, Object arg) { + recordEvent_1Args(Level.ERROR, null, format, arg); + } + + public void error(String format, Object arg1, Object arg2) { + recordEvent2Args(Level.ERROR, null, format, arg1, arg2); + } + + public void error(String format, Object... arguments) { + recordEventArgArray(Level.ERROR, null, format, arguments); + } + + public void error(String msg, Throwable t) { + recordEvent_0Args(Level.ERROR, null, msg, t); + } + + public boolean isErrorEnabled(Marker marker) { + return RECORD_ALL_EVENTS; + } + + public void error(Marker marker, String msg) { + recordEvent_0Args(Level.ERROR, marker, msg, null); + } + + public void error(Marker marker, String format, Object arg) { + recordEvent_1Args(Level.ERROR, marker, format, arg); + } + + public void error(Marker marker, String format, Object arg1, Object arg2) { + recordEvent2Args(Level.ERROR, marker, format, arg1, arg2); + } + + public void error(Marker marker, String format, Object... arguments) { + recordEventArgArray(Level.ERROR, marker, format, arguments); + } + + public void error(Marker marker, String msg, Throwable t) { + recordEvent_0Args(Level.ERROR, marker, msg, t); + } + + private void recordEvent_0Args(Level level, Marker marker, String msg, Throwable t) { + recordEvent(level, marker, msg, null, t); + } + + private void recordEvent_1Args(Level level, Marker marker, String msg, Object arg1) { + recordEvent(level, marker, msg, new Object[] { arg1 }, null); + } + + private void recordEvent2Args(Level level, Marker marker, String msg, Object arg1, Object arg2) { + if (arg2 instanceof Throwable) { + recordEvent(level, marker, msg, new Object[] { arg1 }, (Throwable) arg2); + } else { + recordEvent(level, marker, msg, new Object[] { arg1, arg2 }, null); + } + } + + private void recordEventArgArray(Level level, Marker marker, String msg, Object[] args) { + Throwable throwableCandidate = MessageFormatter.getThrowableCandidate(args); + if (throwableCandidate != null) { + Object[] trimmedCopy = MessageFormatter.trimmedCopy(args); + recordEvent(level, marker, msg, trimmedCopy, throwableCandidate); + } else { + recordEvent(level, marker, msg, args, null); + } + } + + + // WARNING: this method assumes that any throwable is properly extracted + private void recordEvent(Level level, Marker marker, String msg, Object[] args, Throwable throwable) { + SubstituteLoggingEvent loggingEvent = new SubstituteLoggingEvent(); + loggingEvent.setTimeStamp(System.currentTimeMillis()); + loggingEvent.setLevel(level); + loggingEvent.setLogger(logger); + loggingEvent.setLoggerName(name); + loggingEvent.setMarker(marker); + loggingEvent.setMessage(msg); + loggingEvent.setThreadName(Thread.currentThread().getName()); + + loggingEvent.setArgumentArray(args); + loggingEvent.setThrowable(throwable); + + eventQueue.add(loggingEvent); + } +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/event/Level.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/event/Level.java new file mode 100644 index 000000000..5d4031c35 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/event/Level.java @@ -0,0 +1,37 @@ +package org.slf4j.event; + +import static org.slf4j.event.EventConstants.DEBUG_INT; +import static org.slf4j.event.EventConstants.ERROR_INT; +import static org.slf4j.event.EventConstants.INFO_INT; +import static org.slf4j.event.EventConstants.TRACE_INT; +import static org.slf4j.event.EventConstants.WARN_INT; + +/** + * + * @author ceki + * @since 1.7.15 + */ +public enum Level { + + ERROR(ERROR_INT, "ERROR"), WARN(WARN_INT, "WARN"), INFO(INFO_INT, "INFO"), DEBUG(DEBUG_INT, "DEBUG"), TRACE(TRACE_INT, "TRACE"); + + private int levelInt; + private String levelStr; + + Level(int i, String s) { + levelInt = i; + levelStr = s; + } + + public int toInt() { + return levelInt; + } + + /** + * Returns the string representation of this Level. + */ + public String toString() { + return levelStr; + } + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/event/LoggingEvent.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/event/LoggingEvent.java new file mode 100644 index 000000000..1e26c2d4e --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/event/LoggingEvent.java @@ -0,0 +1,28 @@ +package org.slf4j.event; + +import org.slf4j.Marker; + +/** + * + * @author ceki + * @since 1.7.15 + */ +public interface LoggingEvent { + + Level getLevel(); + + Marker getMarker(); + + String getLoggerName(); + + String getMessage(); + + String getThreadName(); + + Object[] getArgumentArray(); + + long getTimeStamp(); + + Throwable getThrowable(); + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/event/SubstituteLoggingEvent.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/event/SubstituteLoggingEvent.java new file mode 100644 index 000000000..8c7acbc2a --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/event/SubstituteLoggingEvent.java @@ -0,0 +1,89 @@ +package org.slf4j.event; + +import org.slf4j.Marker; +import org.slf4j.helpers.SubstituteLogger; + +public class SubstituteLoggingEvent implements LoggingEvent { + + Level level; + Marker marker; + String loggerName; + SubstituteLogger logger; + String threadName; + String message; + Object[] argArray; + long timeStamp; + Throwable throwable; + + public Level getLevel() { + return level; + } + + public void setLevel(Level level) { + this.level = level; + } + + public Marker getMarker() { + return marker; + } + + public void setMarker(Marker marker) { + this.marker = marker; + } + + public String getLoggerName() { + return loggerName; + } + + public void setLoggerName(String loggerName) { + this.loggerName = loggerName; + } + + public SubstituteLogger getLogger() { + return logger; + } + + public void setLogger(SubstituteLogger logger) { + this.logger = logger; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object[] getArgumentArray() { + return argArray; + } + + public void setArgumentArray(Object[] argArray) { + this.argArray = argArray; + } + + public long getTimeStamp() { + return timeStamp; + } + + public void setTimeStamp(long timeStamp) { + this.timeStamp = timeStamp; + } + + public String getThreadName() { + return threadName; + } + + public void setThreadName(String threadName) { + this.threadName = threadName; + } + + public Throwable getThrowable() { + return throwable; + } + + public void setThrowable(Throwable throwable) { + this.throwable = throwable; + } +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/BasicMDCAdapter.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/BasicMDCAdapter.java new file mode 100644 index 000000000..027938d55 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/BasicMDCAdapter.java @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import org.slf4j.spi.MDCAdapter; + +import java.util.*; +import java.util.Map; + +/** + * Basic MDC implementation, which can be used with logging systems that lack + * out-of-the-box MDC support. + * + * This code was initially inspired by logback's LogbackMDCAdapter. However, + * LogbackMDCAdapter has evolved and is now considerably more sophisticated. + * + * @author Ceki Gulcu + * @author Maarten Bosteels + * @author Lukasz Cwik + * + * @since 1.5.0 + */ +public class BasicMDCAdapter implements MDCAdapter { + + private InheritableThreadLocal> inheritableThreadLocal = new InheritableThreadLocal>() { + @Override + protected Map childValue(Map parentValue) { + if (parentValue == null) { + return null; + } + return new HashMap(parentValue); + } + }; + + /** + * Put a context value (the val parameter) as identified with + * the key parameter into the current thread's context map. + * Note that contrary to log4j, the val parameter can be null. + * + *

    + * If the current thread does not have a context map it is created as a side + * effect of this call. + * + * @throws IllegalArgumentException + * in case the "key" parameter is null + */ + public void put(String key, String val) { + if (key == null) { + throw new IllegalArgumentException("key cannot be null"); + } + Map map = inheritableThreadLocal.get(); + if (map == null) { + map = new HashMap(); + inheritableThreadLocal.set(map); + } + map.put(key, val); + } + + /** + * Get the context identified by the key parameter. + */ + public String get(String key) { + Map map = inheritableThreadLocal.get(); + if ((map != null) && (key != null)) { + return map.get(key); + } else { + return null; + } + } + + /** + * Remove the the context identified by the key parameter. + */ + public void remove(String key) { + Map map = inheritableThreadLocal.get(); + if (map != null) { + map.remove(key); + } + } + + /** + * Clear all entries in the MDC. + */ + public void clear() { + Map map = inheritableThreadLocal.get(); + if (map != null) { + map.clear(); + inheritableThreadLocal.remove(); + } + } + + /** + * Returns the keys in the MDC as a {@link Set} of {@link String}s The + * returned value can be null. + * + * @return the keys in the MDC + */ + public Set getKeys() { + Map map = inheritableThreadLocal.get(); + if (map != null) { + return map.keySet(); + } else { + return null; + } + } + + /** + * Return a copy of the current thread's context map. + * Returned value may be null. + * + */ + public Map getCopyOfContextMap() { + Map oldMap = inheritableThreadLocal.get(); + if (oldMap != null) { + return new HashMap(oldMap); + } else { + return null; + } + } + + public void setContextMap(Map contextMap) { + inheritableThreadLocal.set(new HashMap(contextMap)); + } +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/BasicMarker.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/BasicMarker.java new file mode 100644 index 000000000..4daf6404b --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/BasicMarker.java @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.slf4j.Marker; + +/** + * A simple implementation of the {@link Marker} interface. + * + * @author Ceki Gülcü + * @author Joern Huxhorn + */ +public class BasicMarker implements Marker { + + private static final long serialVersionUID = -2849567615646933777L; + private final String name; + private List referenceList = new CopyOnWriteArrayList(); + + BasicMarker(String name) { + if (name == null) { + throw new IllegalArgumentException("A marker name cannot be null"); + } + this.name = name; + } + + public String getName() { + return name; + } + + public void add(Marker reference) { + if (reference == null) { + throw new IllegalArgumentException("A null value cannot be added to a Marker as reference."); + } + + // no point in adding the reference multiple times + if (this.contains(reference)) { + return; + + } else if (reference.contains(this)) { // avoid recursion + // a potential reference should not hold its future "parent" as a reference + return; + } else { + referenceList.add(reference); + } + } + + public boolean hasReferences() { + return (referenceList.size() > 0); + } + + public boolean hasChildren() { + return hasReferences(); + } + + public Iterator iterator() { + return referenceList.iterator(); + } + + public boolean remove(Marker referenceToRemove) { + return referenceList.remove(referenceToRemove); + } + + public boolean contains(Marker other) { + if (other == null) { + throw new IllegalArgumentException("Other cannot be null"); + } + + if (this.equals(other)) { + return true; + } + + if (hasReferences()) { + for (Marker ref : referenceList) { + if (ref.contains(other)) { + return true; + } + } + } + return false; + } + + /** + * This method is mainly used with Expression Evaluators. + */ + public boolean contains(String name) { + if (name == null) { + throw new IllegalArgumentException("Other cannot be null"); + } + + if (this.name.equals(name)) { + return true; + } + + if (hasReferences()) { + for (Marker ref : referenceList) { + if (ref.contains(name)) { + return true; + } + } + } + return false; + } + + private static String OPEN = "[ "; + private static String CLOSE = " ]"; + private static String SEP = ", "; + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof Marker)) + return false; + + final Marker other = (Marker) obj; + return name.equals(other.getName()); + } + + public int hashCode() { + return name.hashCode(); + } + + public String toString() { + if (!this.hasReferences()) { + return this.getName(); + } + Iterator it = this.iterator(); + Marker reference; + StringBuilder sb = new StringBuilder(this.getName()); + sb.append(' ').append(OPEN); + while (it.hasNext()) { + reference = it.next(); + sb.append(reference.getName()); + if (it.hasNext()) { + sb.append(SEP); + } + } + sb.append(CLOSE); + + return sb.toString(); + } +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/BasicMarkerFactory.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/BasicMarkerFactory.java new file mode 100644 index 000000000..5139bb661 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/BasicMarkerFactory.java @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.slf4j.IMarkerFactory; +import org.slf4j.Marker; + +/** + * An almost trivial implementation of the {@link IMarkerFactory} + * interface which creates {@link BasicMarker} instances. + * + *

    Simple logging systems can conform to the SLF4J API by binding + * {@link org.slf4j.MarkerFactory} with an instance of this class. + * + * @author Ceki Gülcü + */ +public class BasicMarkerFactory implements IMarkerFactory { + + private final ConcurrentMap markerMap = new ConcurrentHashMap(); + + /** + * Regular users should not create + * BasicMarkerFactory instances. Marker + * instances can be obtained using the static {@link + * org.slf4j.MarkerFactory#getMarker} method. + */ + public BasicMarkerFactory() { + } + + /** + * Manufacture a {@link BasicMarker} instance by name. If the instance has been + * created earlier, return the previously created instance. + * + * @param name the name of the marker to be created + * @return a Marker instance + */ + public Marker getMarker(String name) { + if (name == null) { + throw new IllegalArgumentException("Marker name cannot be null"); + } + + Marker marker = markerMap.get(name); + if (marker == null) { + marker = new BasicMarker(name); + Marker oldMarker = markerMap.putIfAbsent(name, marker); + if (oldMarker != null) { + marker = oldMarker; + } + } + return marker; + } + + /** + * Does the name marked already exist? + */ + public boolean exists(String name) { + if (name == null) { + return false; + } + return markerMap.containsKey(name); + } + + public boolean detachMarker(String name) { + if (name == null) { + return false; + } + return (markerMap.remove(name) != null); + } + + public Marker getDetachedMarker(String name) { + return new BasicMarker(name); + } + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/FormattingTuple.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/FormattingTuple.java new file mode 100644 index 000000000..6792b1138 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/FormattingTuple.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +/** + * Holds the results of formatting done by {@link MessageFormatter}. + * + * @author Joern Huxhorn + */ +public class FormattingTuple { + + static public FormattingTuple NULL = new FormattingTuple(null); + + private String message; + private Throwable throwable; + private Object[] argArray; + + public FormattingTuple(String message) { + this(message, null, null); + } + + public FormattingTuple(String message, Object[] argArray, Throwable throwable) { + this.message = message; + this.throwable = throwable; + this.argArray = argArray; + } + + public String getMessage() { + return message; + } + + public Object[] getArgArray() { + return argArray; + } + + public Throwable getThrowable() { + return throwable; + } + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/MarkerIgnoringBase.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/MarkerIgnoringBase.java new file mode 100644 index 000000000..dd7653841 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/MarkerIgnoringBase.java @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import org.slf4j.Logger; +import org.slf4j.Marker; + +/** + * This class serves as base for adapters or native implementations of logging systems + * lacking Marker support. In this implementation, methods taking marker data + * simply invoke the corresponding method without the Marker argument, discarding + * any marker data passed as argument. + * + * @author Ceki Gulcu + */ +public abstract class MarkerIgnoringBase extends NamedLoggerBase implements Logger { + + private static final long serialVersionUID = 9044267456635152283L; + + public boolean isTraceEnabled(Marker marker) { + return isTraceEnabled(); + } + + public void trace(Marker marker, String msg) { + trace(msg); + } + + public void trace(Marker marker, String format, Object arg) { + trace(format, arg); + } + + public void trace(Marker marker, String format, Object arg1, Object arg2) { + trace(format, arg1, arg2); + } + + public void trace(Marker marker, String format, Object... arguments) { + trace(format, arguments); + } + + public void trace(Marker marker, String msg, Throwable t) { + trace(msg, t); + } + + public boolean isDebugEnabled(Marker marker) { + return isDebugEnabled(); + } + + public void debug(Marker marker, String msg) { + debug(msg); + } + + public void debug(Marker marker, String format, Object arg) { + debug(format, arg); + } + + public void debug(Marker marker, String format, Object arg1, Object arg2) { + debug(format, arg1, arg2); + } + + public void debug(Marker marker, String format, Object... arguments) { + debug(format, arguments); + } + + public void debug(Marker marker, String msg, Throwable t) { + debug(msg, t); + } + + public boolean isInfoEnabled(Marker marker) { + return isInfoEnabled(); + } + + public void info(Marker marker, String msg) { + info(msg); + } + + public void info(Marker marker, String format, Object arg) { + info(format, arg); + } + + public void info(Marker marker, String format, Object arg1, Object arg2) { + info(format, arg1, arg2); + } + + public void info(Marker marker, String format, Object... arguments) { + info(format, arguments); + } + + public void info(Marker marker, String msg, Throwable t) { + info(msg, t); + } + + public boolean isWarnEnabled(Marker marker) { + return isWarnEnabled(); + } + + public void warn(Marker marker, String msg) { + warn(msg); + } + + public void warn(Marker marker, String format, Object arg) { + warn(format, arg); + } + + public void warn(Marker marker, String format, Object arg1, Object arg2) { + warn(format, arg1, arg2); + } + + public void warn(Marker marker, String format, Object... arguments) { + warn(format, arguments); + } + + public void warn(Marker marker, String msg, Throwable t) { + warn(msg, t); + } + + public boolean isErrorEnabled(Marker marker) { + return isErrorEnabled(); + } + + public void error(Marker marker, String msg) { + error(msg); + } + + public void error(Marker marker, String format, Object arg) { + error(format, arg); + } + + public void error(Marker marker, String format, Object arg1, Object arg2) { + error(format, arg1, arg2); + } + + public void error(Marker marker, String format, Object... arguments) { + error(format, arguments); + } + + public void error(Marker marker, String msg, Throwable t) { + error(msg, t); + } + + public String toString() { + return this.getClass().getName() + "(" + getName() + ")"; + } + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/MessageFormatter.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/MessageFormatter.java new file mode 100644 index 000000000..d23a3a63c --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/MessageFormatter.java @@ -0,0 +1,437 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +// contributors: lizongbo: proposed special treatment of array parameter values +// Joern Huxhorn: pointed out double[] omission, suggested deep array copy +/** + * Formats messages according to very simple substitution rules. Substitutions + * can be made 1, 2 or more arguments. + * + *

    + * For example, + * + *

    + * MessageFormatter.format("Hi {}.", "there")
    + * 
    + * + * will return the string "Hi there.". + *

    + * The {} pair is called the formatting anchor. It serves to designate + * the location where arguments need to be substituted within the message + * pattern. + *

    + * In case your message contains the '{' or the '}' character, you do not have + * to do anything special unless the '}' character immediately follows '{'. For + * example, + * + *

    + * MessageFormatter.format("Set {1,2,3} is not equal to {}.", "1,2");
    + * 
    + * + * will return the string "Set {1,2,3} is not equal to 1,2.". + * + *

    + * If for whatever reason you need to place the string "{}" in the message + * without its formatting anchor meaning, then you need to escape the + * '{' character with '\', that is the backslash character. Only the '{' + * character should be escaped. There is no need to escape the '}' character. + * For example, + * + *

    + * MessageFormatter.format("Set \\{} is not equal to {}.", "1,2");
    + * 
    + * + * will return the string "Set {} is not equal to 1,2.". + * + *

    + * The escaping behavior just described can be overridden by escaping the escape + * character '\'. Calling + * + *

    + * MessageFormatter.format("File name is C:\\\\{}.", "file.zip");
    + * 
    + * + * will return the string "File name is C:\file.zip". + * + *

    + * The formatting conventions are different than those of {@link MessageFormat} + * which ships with the Java platform. This is justified by the fact that + * SLF4J's implementation is 10 times faster than that of {@link MessageFormat}. + * This local performance difference is both measurable and significant in the + * larger context of the complete logging processing chain. + * + *

    + * See also {@link #format(String, Object)}, + * {@link #format(String, Object, Object)} and + * {@link #arrayFormat(String, Object[])} methods for more details. + * + * @author Ceki Gülcü + * @author Joern Huxhorn + */ +final public class MessageFormatter { + static final char DELIM_START = '{'; + static final char DELIM_STOP = '}'; + static final String DELIM_STR = "{}"; + private static final char ESCAPE_CHAR = '\\'; + + /** + * Performs single argument substitution for the 'messagePattern' passed as + * parameter. + *

    + * For example, + * + *

    +     * MessageFormatter.format("Hi {}.", "there");
    +     * 
    + * + * will return the string "Hi there.". + *

    + * + * @param messagePattern + * The message pattern which will be parsed and formatted + * @param arg + * The argument to be substituted in place of the formatting anchor + * @return The formatted message + */ + final public static FormattingTuple format(String messagePattern, Object arg) { + return arrayFormat(messagePattern, new Object[] { arg }); + } + + /** + * + * Performs a two argument substitution for the 'messagePattern' passed as + * parameter. + *

    + * For example, + * + *

    +     * MessageFormatter.format("Hi {}. My name is {}.", "Alice", "Bob");
    +     * 
    + * + * will return the string "Hi Alice. My name is Bob.". + * + * @param messagePattern + * The message pattern which will be parsed and formatted + * @param arg1 + * The argument to be substituted in place of the first formatting + * anchor + * @param arg2 + * The argument to be substituted in place of the second formatting + * anchor + * @return The formatted message + */ + final public static FormattingTuple format(final String messagePattern, Object arg1, Object arg2) { + return arrayFormat(messagePattern, new Object[] { arg1, arg2 }); + } + + + final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray) { + Throwable throwableCandidate = MessageFormatter.getThrowableCandidate(argArray); + Object[] args = argArray; + if (throwableCandidate != null) { + args = MessageFormatter.trimmedCopy(argArray); + } + return arrayFormat(messagePattern, args, throwableCandidate); + } + + final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray, Throwable throwable) { + + if (messagePattern == null) { + return new FormattingTuple(null, argArray, throwable); + } + + if (argArray == null) { + return new FormattingTuple(messagePattern); + } + + int i = 0; + int j; + // use string builder for better multicore performance + StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50); + + int L; + for (L = 0; L < argArray.length; L++) { + + j = messagePattern.indexOf(DELIM_STR, i); + + if (j == -1) { + // no more variables + if (i == 0) { // this is a simple string + return new FormattingTuple(messagePattern, argArray, throwable); + } else { // add the tail string which contains no variables and return + // the result. + sbuf.append(messagePattern, i, messagePattern.length()); + return new FormattingTuple(sbuf.toString(), argArray, throwable); + } + } else { + if (isEscapedDelimeter(messagePattern, j)) { + if (!isDoubleEscaped(messagePattern, j)) { + L--; // DELIM_START was escaped, thus should not be incremented + sbuf.append(messagePattern, i, j - 1); + sbuf.append(DELIM_START); + i = j + 1; + } else { + // The escape character preceding the delimiter start is + // itself escaped: "abc x:\\{}" + // we have to consume one backward slash + sbuf.append(messagePattern, i, j - 1); + deeplyAppendParameter(sbuf, argArray[L], new HashMap()); + i = j + 2; + } + } else { + // normal case + sbuf.append(messagePattern, i, j); + deeplyAppendParameter(sbuf, argArray[L], new HashMap()); + i = j + 2; + } + } + } + // append the characters following the last {} pair. + sbuf.append(messagePattern, i, messagePattern.length()); + return new FormattingTuple(sbuf.toString(), argArray, throwable); + } + + final static boolean isEscapedDelimeter(String messagePattern, int delimeterStartIndex) { + + if (delimeterStartIndex == 0) { + return false; + } + char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1); + if (potentialEscape == ESCAPE_CHAR) { + return true; + } else { + return false; + } + } + + final static boolean isDoubleEscaped(String messagePattern, int delimeterStartIndex) { + if (delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) { + return true; + } else { + return false; + } + } + + // special treatment of array values was suggested by 'lizongbo' + private static void deeplyAppendParameter(StringBuilder sbuf, Object o, Map seenMap) { + if (o == null) { + sbuf.append("null"); + return; + } + if (!o.getClass().isArray()) { + safeObjectAppend(sbuf, o); + } else { + // check for primitive array types because they + // unfortunately cannot be cast to Object[] + if (o instanceof boolean[]) { + booleanArrayAppend(sbuf, (boolean[]) o); + } else if (o instanceof byte[]) { + byteArrayAppend(sbuf, (byte[]) o); + } else if (o instanceof char[]) { + charArrayAppend(sbuf, (char[]) o); + } else if (o instanceof short[]) { + shortArrayAppend(sbuf, (short[]) o); + } else if (o instanceof int[]) { + intArrayAppend(sbuf, (int[]) o); + } else if (o instanceof long[]) { + longArrayAppend(sbuf, (long[]) o); + } else if (o instanceof float[]) { + floatArrayAppend(sbuf, (float[]) o); + } else if (o instanceof double[]) { + doubleArrayAppend(sbuf, (double[]) o); + } else { + objectArrayAppend(sbuf, (Object[]) o, seenMap); + } + } + } + + private static void safeObjectAppend(StringBuilder sbuf, Object o) { + try { + String oAsString = o.toString(); + sbuf.append(oAsString); + } catch (Throwable t) { + Util.report("SLF4J: Failed toString() invocation on an object of type [" + o.getClass().getName() + "]", t); + sbuf.append("[FAILED toString()]"); + } + + } + + private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Map seenMap) { + sbuf.append('['); + if (!seenMap.containsKey(a)) { + seenMap.put(a, null); + final int len = a.length; + for (int i = 0; i < len; i++) { + deeplyAppendParameter(sbuf, a[i], seenMap); + if (i != len - 1) + sbuf.append(", "); + } + // allow repeats in siblings + seenMap.remove(a); + } else { + sbuf.append("..."); + } + sbuf.append(']'); + } + + private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) + sbuf.append(", "); + } + sbuf.append(']'); + } + + private static void byteArrayAppend(StringBuilder sbuf, byte[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) + sbuf.append(", "); + } + sbuf.append(']'); + } + + private static void charArrayAppend(StringBuilder sbuf, char[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) + sbuf.append(", "); + } + sbuf.append(']'); + } + + private static void shortArrayAppend(StringBuilder sbuf, short[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) + sbuf.append(", "); + } + sbuf.append(']'); + } + + private static void intArrayAppend(StringBuilder sbuf, int[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) + sbuf.append(", "); + } + sbuf.append(']'); + } + + private static void longArrayAppend(StringBuilder sbuf, long[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) + sbuf.append(", "); + } + sbuf.append(']'); + } + + private static void floatArrayAppend(StringBuilder sbuf, float[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) + sbuf.append(", "); + } + sbuf.append(']'); + } + + private static void doubleArrayAppend(StringBuilder sbuf, double[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) + sbuf.append(", "); + } + sbuf.append(']'); + } + + /** + * Helper method to determine if an {@link Object} array contains a {@link Throwable} as last element + * + * @param argArray + * The arguments off which we want to know if it contains a {@link Throwable} as last element + * @return if the last {@link Object} in argArray is a {@link Throwable} this method will return it, + * otherwise it returns null + */ + public static Throwable getThrowableCandidate(final Object[] argArray) { + if (argArray == null || argArray.length == 0) { + return null; + } + + final Object lastEntry = argArray[argArray.length - 1]; + if (lastEntry instanceof Throwable) { + return (Throwable) lastEntry; + } + + return null; + } + + /** + * Helper method to get all but the last element of an array + * + * @param argArray + * The arguments from which we want to remove the last element + * + * @return a copy of the array without the last element + */ + public static Object[] trimmedCopy(final Object[] argArray) { + if (argArray == null || argArray.length == 0) { + throw new IllegalStateException("non-sensical empty or null argument array"); + } + + final int trimmedLen = argArray.length - 1; + + Object[] trimmed = new Object[trimmedLen]; + + if (trimmedLen > 0) { + System.arraycopy(argArray, 0, trimmed, 0, trimmedLen); + } + + return trimmed; + } + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NOPLogger.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NOPLogger.java new file mode 100644 index 000000000..b98435fe1 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NOPLogger.java @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import org.slf4j.Logger; +import org.slf4j.helpers.MarkerIgnoringBase; + +/** + * A direct NOP (no operation) implementation of {@link Logger}. + * + * @author Ceki Gülcü + */ +public class NOPLogger extends MarkerIgnoringBase { + + private static final long serialVersionUID = -517220405410904473L; + + /** + * The unique instance of NOPLogger. + */ + public static final NOPLogger NOP_LOGGER = new NOPLogger(); + + /** + * There is no point in creating multiple instances of NOPLogger, + * except by derived classes, hence the protected access for the constructor. + */ + protected NOPLogger() { + } + + /** + * Always returns the string value "NOP". + */ + public String getName() { + return "NOP"; + } + + /** + * Always returns false. + * @return always false + */ + final public boolean isTraceEnabled() { + return false; + } + + /** A NOP implementation. */ + final public void trace(String msg) { + // NOP + } + + /** A NOP implementation. */ + final public void trace(String format, Object arg) { + // NOP + } + + /** A NOP implementation. */ + public final void trace(String format, Object arg1, Object arg2) { + // NOP + } + + /** A NOP implementation. */ + public final void trace(String format, Object... argArray) { + // NOP + } + + /** A NOP implementation. */ + final public void trace(String msg, Throwable t) { + // NOP + } + + /** + * Always returns false. + * @return always false + */ + final public boolean isDebugEnabled() { + return false; + } + + /** A NOP implementation. */ + final public void debug(String msg) { + // NOP + } + + /** A NOP implementation. */ + final public void debug(String format, Object arg) { + // NOP + } + + /** A NOP implementation. */ + public final void debug(String format, Object arg1, Object arg2) { + // NOP + } + + /** A NOP implementation. */ + public final void debug(String format, Object... argArray) { + // NOP + } + + /** A NOP implementation. */ + final public void debug(String msg, Throwable t) { + // NOP + } + + /** + * Always returns false. + * @return always false + */ + final public boolean isInfoEnabled() { + // NOP + return false; + } + + /** A NOP implementation. */ + final public void info(String msg) { + // NOP + } + + /** A NOP implementation. */ + final public void info(String format, Object arg1) { + // NOP + } + + /** A NOP implementation. */ + final public void info(String format, Object arg1, Object arg2) { + // NOP + } + + /** A NOP implementation. */ + public final void info(String format, Object... argArray) { + // NOP + } + + /** A NOP implementation. */ + final public void info(String msg, Throwable t) { + // NOP + } + + /** + * Always returns false. + * @return always false + */ + final public boolean isWarnEnabled() { + return false; + } + + /** A NOP implementation. */ + final public void warn(String msg) { + // NOP + } + + /** A NOP implementation. */ + final public void warn(String format, Object arg1) { + // NOP + } + + /** A NOP implementation. */ + final public void warn(String format, Object arg1, Object arg2) { + // NOP + } + + /** A NOP implementation. */ + public final void warn(String format, Object... argArray) { + // NOP + } + + /** A NOP implementation. */ + final public void warn(String msg, Throwable t) { + // NOP + } + + /** A NOP implementation. */ + final public boolean isErrorEnabled() { + return false; + } + + /** A NOP implementation. */ + final public void error(String msg) { + // NOP + } + + /** A NOP implementation. */ + final public void error(String format, Object arg1) { + // NOP + } + + /** A NOP implementation. */ + final public void error(String format, Object arg1, Object arg2) { + // NOP + } + + /** A NOP implementation. */ + public final void error(String format, Object... argArray) { + // NOP + } + + /** A NOP implementation. */ + final public void error(String msg, Throwable t) { + // NOP + } +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NOPLoggerFactory.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NOPLoggerFactory.java new file mode 100644 index 000000000..b8a55dc9b --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NOPLoggerFactory.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; +import org.slf4j.helpers.NOPLogger; + +/** + * NOPLoggerFactory is an trivial implementation of {@link + * ILoggerFactory} which always returns the unique instance of + * NOPLogger. + * + * @author Ceki Gülcü + */ +public class NOPLoggerFactory implements ILoggerFactory { + + public NOPLoggerFactory() { + // nothing to do + } + + public Logger getLogger(String name) { + return NOPLogger.NOP_LOGGER; + } + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NOPMDCAdapter.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NOPMDCAdapter.java new file mode 100644 index 000000000..22f3ea45e --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NOPMDCAdapter.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import java.util.Map; + +import org.slf4j.spi.MDCAdapter; + +/** + * This adapter is an empty implementation of the {@link MDCAdapter} interface. + * It is used for all logging systems which do not support mapped + * diagnostic contexts such as JDK14, simple and NOP. + * + * @author Ceki Gülcü + * + * @since 1.4.1 + */ +public class NOPMDCAdapter implements MDCAdapter { + + public void clear() { + } + + public String get(String key) { + return null; + } + + public void put(String key, String val) { + } + + public void remove(String key) { + } + + public Map getCopyOfContextMap() { + return null; + } + + public void setContextMap(Map contextMap) { + // NOP + } + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NamedLoggerBase.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NamedLoggerBase.java new file mode 100644 index 000000000..e2a1d068d --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/NamedLoggerBase.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import java.io.ObjectStreamException; +import java.io.Serializable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Serves as base class for named logger implementation. More significantly, this + * class establishes deserialization behavior. + * + * @author Ceki Gulcu + * @see #readResolve + * @since 1.5.3 + */ +abstract class NamedLoggerBase implements Logger, Serializable { + + private static final long serialVersionUID = 7535258609338176893L; + + protected String name; + + public String getName() { + return name; + } + + /** + * Replace this instance with a homonymous (same name) logger returned + * by LoggerFactory. Note that this method is only called during + * deserialization. + * + *

    + * This approach will work well if the desired ILoggerFactory is the one + * references by LoggerFactory. However, if the user manages its logger hierarchy + * through a different (non-static) mechanism, e.g. dependency injection, then + * this approach would be mostly counterproductive. + * + * @return logger with same name as returned by LoggerFactory + * @throws ObjectStreamException + */ + protected Object readResolve() throws ObjectStreamException { + // using getName() instead of this.name works even for + // NOPLogger + return LoggerFactory.getLogger(getName()); + } + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/SubstituteLogger.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/SubstituteLogger.java new file mode 100644 index 000000000..3ac0d32b5 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/SubstituteLogger.java @@ -0,0 +1,390 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Queue; + +import org.slf4j.Logger; +import org.slf4j.Marker; +import org.slf4j.event.EventRecodingLogger; +import org.slf4j.event.LoggingEvent; +import org.slf4j.event.SubstituteLoggingEvent; + +/** + * A logger implementation which logs via a delegate logger. By default, the delegate is a + * {@link NOPLogger}. However, a different delegate can be set at any time. + *

    + * See also the relevant + * error code documentation. + * + * @author Chetan Mehrotra + * @author Ceki Gulcu + */ +public class SubstituteLogger implements Logger { + + private final String name; + private volatile Logger _delegate; + private Boolean delegateEventAware; + private Method logMethodCache; + private EventRecodingLogger eventRecodingLogger; + private Queue eventQueue; + + private final boolean createdPostInitialization; + + public SubstituteLogger(String name, Queue eventQueue, boolean createdPostInitialization) { + this.name = name; + this.eventQueue = eventQueue; + this.createdPostInitialization = createdPostInitialization; + } + + public String getName() { + return name; + } + + public boolean isTraceEnabled() { + return delegate().isTraceEnabled(); + } + + public void trace(String msg) { + delegate().trace(msg); + } + + public void trace(String format, Object arg) { + delegate().trace(format, arg); + } + + public void trace(String format, Object arg1, Object arg2) { + delegate().trace(format, arg1, arg2); + } + + public void trace(String format, Object... arguments) { + delegate().trace(format, arguments); + } + + public void trace(String msg, Throwable t) { + delegate().trace(msg, t); + } + + public boolean isTraceEnabled(Marker marker) { + return delegate().isTraceEnabled(marker); + } + + public void trace(Marker marker, String msg) { + delegate().trace(marker, msg); + } + + public void trace(Marker marker, String format, Object arg) { + delegate().trace(marker, format, arg); + } + + public void trace(Marker marker, String format, Object arg1, Object arg2) { + delegate().trace(marker, format, arg1, arg2); + } + + public void trace(Marker marker, String format, Object... arguments) { + delegate().trace(marker, format, arguments); + } + + public void trace(Marker marker, String msg, Throwable t) { + delegate().trace(marker, msg, t); + } + + public boolean isDebugEnabled() { + return delegate().isDebugEnabled(); + } + + public void debug(String msg) { + delegate().debug(msg); + } + + public void debug(String format, Object arg) { + delegate().debug(format, arg); + } + + public void debug(String format, Object arg1, Object arg2) { + delegate().debug(format, arg1, arg2); + } + + public void debug(String format, Object... arguments) { + delegate().debug(format, arguments); + } + + public void debug(String msg, Throwable t) { + delegate().debug(msg, t); + } + + public boolean isDebugEnabled(Marker marker) { + return delegate().isDebugEnabled(marker); + } + + public void debug(Marker marker, String msg) { + delegate().debug(marker, msg); + } + + public void debug(Marker marker, String format, Object arg) { + delegate().debug(marker, format, arg); + } + + public void debug(Marker marker, String format, Object arg1, Object arg2) { + delegate().debug(marker, format, arg1, arg2); + } + + public void debug(Marker marker, String format, Object... arguments) { + delegate().debug(marker, format, arguments); + } + + public void debug(Marker marker, String msg, Throwable t) { + delegate().debug(marker, msg, t); + } + + public boolean isInfoEnabled() { + return delegate().isInfoEnabled(); + } + + public void info(String msg) { + delegate().info(msg); + } + + public void info(String format, Object arg) { + delegate().info(format, arg); + } + + public void info(String format, Object arg1, Object arg2) { + delegate().info(format, arg1, arg2); + } + + public void info(String format, Object... arguments) { + delegate().info(format, arguments); + } + + public void info(String msg, Throwable t) { + delegate().info(msg, t); + } + + public boolean isInfoEnabled(Marker marker) { + return delegate().isInfoEnabled(marker); + } + + public void info(Marker marker, String msg) { + delegate().info(marker, msg); + } + + public void info(Marker marker, String format, Object arg) { + delegate().info(marker, format, arg); + } + + public void info(Marker marker, String format, Object arg1, Object arg2) { + delegate().info(marker, format, arg1, arg2); + } + + public void info(Marker marker, String format, Object... arguments) { + delegate().info(marker, format, arguments); + } + + public void info(Marker marker, String msg, Throwable t) { + delegate().info(marker, msg, t); + } + + public boolean isWarnEnabled() { + return delegate().isWarnEnabled(); + } + + public void warn(String msg) { + delegate().warn(msg); + } + + public void warn(String format, Object arg) { + delegate().warn(format, arg); + } + + public void warn(String format, Object arg1, Object arg2) { + delegate().warn(format, arg1, arg2); + } + + public void warn(String format, Object... arguments) { + delegate().warn(format, arguments); + } + + public void warn(String msg, Throwable t) { + delegate().warn(msg, t); + } + + public boolean isWarnEnabled(Marker marker) { + return delegate().isWarnEnabled(marker); + } + + public void warn(Marker marker, String msg) { + delegate().warn(marker, msg); + } + + public void warn(Marker marker, String format, Object arg) { + delegate().warn(marker, format, arg); + } + + public void warn(Marker marker, String format, Object arg1, Object arg2) { + delegate().warn(marker, format, arg1, arg2); + } + + public void warn(Marker marker, String format, Object... arguments) { + delegate().warn(marker, format, arguments); + } + + public void warn(Marker marker, String msg, Throwable t) { + delegate().warn(marker, msg, t); + } + + public boolean isErrorEnabled() { + return delegate().isErrorEnabled(); + } + + public void error(String msg) { + delegate().error(msg); + } + + public void error(String format, Object arg) { + delegate().error(format, arg); + } + + public void error(String format, Object arg1, Object arg2) { + delegate().error(format, arg1, arg2); + } + + public void error(String format, Object... arguments) { + delegate().error(format, arguments); + } + + public void error(String msg, Throwable t) { + delegate().error(msg, t); + } + + public boolean isErrorEnabled(Marker marker) { + return delegate().isErrorEnabled(marker); + } + + public void error(Marker marker, String msg) { + delegate().error(marker, msg); + } + + public void error(Marker marker, String format, Object arg) { + delegate().error(marker, format, arg); + } + + public void error(Marker marker, String format, Object arg1, Object arg2) { + delegate().error(marker, format, arg1, arg2); + } + + public void error(Marker marker, String format, Object... arguments) { + delegate().error(marker, format, arguments); + } + + public void error(Marker marker, String msg, Throwable t) { + delegate().error(marker, msg, t); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + SubstituteLogger that = (SubstituteLogger) o; + + if (!name.equals(that.name)) + return false; + + return true; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + /** + * Return the delegate logger instance if set. Otherwise, return a {@link NOPLogger} + * instance. + */ + Logger delegate() { + if(_delegate != null) { + return _delegate; + } + if(createdPostInitialization) { + return NOPLogger.NOP_LOGGER; + } else { + return getEventRecordingLogger(); + } + } + + private Logger getEventRecordingLogger() { + if (eventRecodingLogger == null) { + eventRecodingLogger = new EventRecodingLogger(this, eventQueue); + } + return eventRecodingLogger; + } + + /** + * Typically called after the {@link org.slf4j.LoggerFactory} initialization phase is completed. + * @param delegate + */ + public void setDelegate(Logger delegate) { + this._delegate = delegate; + } + + public boolean isDelegateEventAware() { + if (delegateEventAware != null) + return delegateEventAware; + + try { + logMethodCache = _delegate.getClass().getMethod("log", LoggingEvent.class); + delegateEventAware = Boolean.TRUE; + } catch (NoSuchMethodException e) { + delegateEventAware = Boolean.FALSE; + } + return delegateEventAware; + } + + public void log(LoggingEvent event) { + if (isDelegateEventAware()) { + try { + logMethodCache.invoke(_delegate, event); + } catch (IllegalAccessException e) { + } catch (IllegalArgumentException e) { + } catch (InvocationTargetException e) { + } + } + } + + + public boolean isDelegateNull() { + return _delegate == null; + } + + public boolean isDelegateNOP() { + return _delegate instanceof NOPLogger; + } +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/SubstituteLoggerFactory.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/SubstituteLoggerFactory.java new file mode 100644 index 000000000..0fb6114b1 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/SubstituteLoggerFactory.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; + +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; +import org.slf4j.event.SubstituteLoggingEvent; + +/** + * SubstituteLoggerFactory manages instances of {@link SubstituteLogger}. + * + * @author Ceki Gülcü + * @author Chetan Mehrotra + */ +public class SubstituteLoggerFactory implements ILoggerFactory { + + boolean postInitialization = false; + + final Map loggers = new HashMap(); + + final LinkedBlockingQueue eventQueue = new LinkedBlockingQueue(); + + synchronized public Logger getLogger(String name) { + SubstituteLogger logger = loggers.get(name); + if (logger == null) { + logger = new SubstituteLogger(name, eventQueue, postInitialization); + loggers.put(name, logger); + } + return logger; + } + + public List getLoggerNames() { + return new ArrayList(loggers.keySet()); + } + + public List getLoggers() { + return new ArrayList(loggers.values()); + } + + public LinkedBlockingQueue getEventQueue() { + return eventQueue; + } + + public void postInitialization() { + postInitialization = true; + } + + public void clear() { + loggers.clear(); + eventQueue.clear(); + } +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/Util.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/Util.java new file mode 100644 index 000000000..06017849b --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/Util.java @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.helpers; + +/** + * An internal utility class. + * + * @author Alexander Dorokhine + * @author Ceki Gülcü + */ +public final class Util { + + + private Util() { + } + + public static String safeGetSystemProperty(String key) { + if (key == null) + throw new IllegalArgumentException("null input"); + + String result = null; + try { + result = System.getProperty(key); + } catch (java.lang.SecurityException sm) { + ; // ignore + } + return result; + } + + public static boolean safeGetBooleanSystemProperty(String key) { + String value = safeGetSystemProperty(key); + if (value == null) + return false; + else + return value.equalsIgnoreCase("true"); + } + + /** + * In order to call {@link SecurityManager#getClassContext()}, which is a + * protected method, we add this wrapper which allows the method to be visible + * inside this package. + */ + private static final class ClassContextSecurityManager extends SecurityManager { + protected Class[] getClassContext() { + return super.getClassContext(); + } + } + + private static ClassContextSecurityManager SECURITY_MANAGER; + private static boolean SECURITY_MANAGER_CREATION_ALREADY_ATTEMPTED = false; + + private static ClassContextSecurityManager getSecurityManager() { + if (SECURITY_MANAGER != null) + return SECURITY_MANAGER; + else if (SECURITY_MANAGER_CREATION_ALREADY_ATTEMPTED) + return null; + else { + SECURITY_MANAGER = safeCreateSecurityManager(); + SECURITY_MANAGER_CREATION_ALREADY_ATTEMPTED = true; + return SECURITY_MANAGER; + } + } + + private static ClassContextSecurityManager safeCreateSecurityManager() { + try { + return new ClassContextSecurityManager(); + } catch (java.lang.SecurityException sm) { + return null; + } + } + + /** + * Returns the name of the class which called the invoking method. + * + * @return the name of the class which called the invoking method. + */ + public static Class getCallingClass() { + ClassContextSecurityManager securityManager = getSecurityManager(); + if (securityManager == null) + return null; + Class[] trace = securityManager.getClassContext(); + String thisClassName = Util.class.getName(); + + // Advance until Util is found + int i; + for (i = 0; i < trace.length; i++) { + if (thisClassName.equals(trace[i].getName())) + break; + } + + // trace[i] = Util; trace[i+1] = caller; trace[i+2] = caller's caller + if (i >= trace.length || i + 2 >= trace.length) { + throw new IllegalStateException("Failed to find org.slf4j.helpers.Util or its caller in the stack; " + "this should not happen"); + } + + return trace[i + 2]; + } + + static final public void report(String msg, Throwable t) { + System.err.println(msg); + System.err.println("Reported exception:"); + t.printStackTrace(); + } + + static final public void report(String msg) { + System.err.println("SLF4J: " + msg); + } + + + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/package.html b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/package.html new file mode 100644 index 000000000..f223543d4 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/helpers/package.html @@ -0,0 +1,16 @@ + + + + + + + + + + + +

    Helper classes.

    + +
    + + diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/StaticLoggerBinder.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/StaticLoggerBinder.java new file mode 100644 index 000000000..2eda15c77 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/StaticLoggerBinder.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.impl; + +import org.slf4j.ILoggerFactory; + +/** + * The binding of {@link org.slf4j.LoggerFactory} class with an actual instance of + * {@link ILoggerFactory} is performed using information returned by this class. + * + * This class is meant to provide a dummy StaticLoggerBinder to the slf4j-api module. + * Real implementations are found in each SLF4J binding project, e.g. slf4j-nop, + * slf4j-log4j12 etc. + * + * @author Ceki Gülcü + */ +public class StaticLoggerBinder { + + /** + * The unique instance of this class. + */ + private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); + + /** + * Return the singleton of this class. + * + * @return the StaticLoggerBinder singleton + */ + public static final StaticLoggerBinder getSingleton() { + return SINGLETON; + } + + /** + * Declare the version of the SLF4J API this implementation is compiled against. + * The value of this field is modified with each major release. + */ + // to avoid constant folding by the compiler, this field must *not* be final + public static String REQUESTED_API_VERSION = "1.6.99"; // !final + + private StaticLoggerBinder() { + throw new UnsupportedOperationException("This code should have never made it into slf4j-api.jar"); + } + + public ILoggerFactory getLoggerFactory() { + throw new UnsupportedOperationException("This code should never make it into slf4j-api.jar"); + } + + public String getLoggerFactoryClassStr() { + throw new UnsupportedOperationException("This code should never make it into slf4j-api.jar"); + } +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/StaticMDCBinder.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/StaticMDCBinder.java new file mode 100644 index 000000000..b9560d41c --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/StaticMDCBinder.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.impl; + +import org.slf4j.spi.MDCAdapter; + +/** + * This class is only a stub. Real implementations are found in + * each SLF4J binding project, e.g. slf4j-nop, slf4j-log4j12 etc. + * + * @author Ceki Gülcü + */ +public class StaticMDCBinder { + + /** + * The unique instance of this class. + */ + public static final StaticMDCBinder SINGLETON = new StaticMDCBinder(); + + private StaticMDCBinder() { + throw new UnsupportedOperationException("This code should never make it into the jar"); + } + + /** + * Return the singleton of this class. + * + * @return the StaticMDCBinder singleton + * @since 1.7.14 + */ + public static final StaticMDCBinder getSingleton() { + return SINGLETON; + } + + /** + * Currently this method always returns an instance of + * {@link StaticMDCBinder}. + */ + public MDCAdapter getMDCA() { + throw new UnsupportedOperationException("This code should never make it into the jar"); + } + + public String getMDCAdapterClassStr() { + throw new UnsupportedOperationException("This code should never make it into the jar"); + } +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/StaticMarkerBinder.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/StaticMarkerBinder.java new file mode 100644 index 000000000..6669770f0 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/StaticMarkerBinder.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.impl; + +import org.slf4j.IMarkerFactory; +import org.slf4j.MarkerFactory; +import org.slf4j.helpers.BasicMarkerFactory; +import org.slf4j.spi.MarkerFactoryBinder; + +/** + * + * The binding of {@link MarkerFactory} class with an actual instance of + * {@link IMarkerFactory} is performed using information returned by this class. + * + * This class is meant to provide a *dummy* StaticMarkerBinder to the slf4j-api module. + * Real implementations are found in each SLF4J binding project, e.g. slf4j-nop, + * slf4j-simple, slf4j-log4j12 etc. + * + * @author Ceki Gülcü + */ +public class StaticMarkerBinder implements MarkerFactoryBinder { + + /** + * The unique instance of this class. + */ + public static final StaticMarkerBinder SINGLETON = new StaticMarkerBinder(); + + private StaticMarkerBinder() { + throw new UnsupportedOperationException("This code should never make it into the jar"); + } + + /** + * Return the singleton of this class. + * + * @return the StaticMarkerBinder singleton + * @since 1.7.14 + */ + public static StaticMarkerBinder getSingleton() { + return SINGLETON; + } + + /** + * Currently this method always returns an instance of + * {@link BasicMarkerFactory}. + */ + public IMarkerFactory getMarkerFactory() { + throw new UnsupportedOperationException("This code should never make it into the jar"); + } + + /** + * Currently, this method returns the class name of + * {@link BasicMarkerFactory}. + */ + public String getMarkerFactoryClassStr() { + throw new UnsupportedOperationException("This code should never make it into the jar"); + } + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/package.html b/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/package.html new file mode 100644 index 000000000..6b84bada6 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/impl/package.html @@ -0,0 +1,17 @@ + + + + + + + + + + + +

    Implementations of core logging interfaces defined in the {@link + org.slf4j} package.

    + +
    + + diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/package.html b/fine-third-default/fine-slf4j-api/src/org/slf4j/package.html new file mode 100644 index 000000000..e50b3ee27 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/package.html @@ -0,0 +1,16 @@ + + + + + + + + + + + +

    Core logging interfaces.

    + +
    + + diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/LocationAwareLogger.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/LocationAwareLogger.java new file mode 100644 index 000000000..d338787df --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/LocationAwareLogger.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.spi; + +import org.slf4j.Logger; +import org.slf4j.Marker; + +/** + * An optional interface helping integration with logging systems capable of + * extracting location information. This interface is mainly used by SLF4J bridges + * such as jcl-over-slf4j, jul-to-slf4j and log4j-over-slf4j or {@link Logger} wrappers + * which need to provide hints so that the underlying logging system can extract + * the correct location information (method name, line number). + * + * @author Ceki Gulcu + * @since 1.3 + */ +public interface LocationAwareLogger extends Logger { + + // these constants should be in EventContants. However, in order to preserve binary backward compatibility + // we keep these constants here + final public int TRACE_INT = 00; + final public int DEBUG_INT = 10; + final public int INFO_INT = 20; + final public int WARN_INT = 30; + final public int ERROR_INT = 40; + + /** + * Printing method with support for location information. + * + * @param marker The marker to be used for this event, may be null. + * @param fqcn The fully qualified class name of the logger instance, + * typically the logger class, logger bridge or a logger wrapper. + * @param level One of the level integers defined in this interface + * @param message The message for the log event + * @param t Throwable associated with the log event, may be null. + */ + public void log(Marker marker, String fqcn, int level, String message, Object[] argArray, Throwable t); + +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/LoggerFactoryBinder.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/LoggerFactoryBinder.java new file mode 100644 index 000000000..9316b4db9 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/LoggerFactoryBinder.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.spi; + +import org.slf4j.ILoggerFactory; + +/** + * An internal interface which helps the static {@link org.slf4j.LoggerFactory} + * class bind with the appropriate {@link ILoggerFactory} instance. + * + * @author Ceki Gülcü + */ +public interface LoggerFactoryBinder { + + /** + * Return the instance of {@link ILoggerFactory} that + * {@link org.slf4j.LoggerFactory} class should bind to. + * + * @return the instance of {@link ILoggerFactory} that + * {@link org.slf4j.LoggerFactory} class should bind to. + */ + public ILoggerFactory getLoggerFactory(); + + /** + * The String form of the {@link ILoggerFactory} object that this + * LoggerFactoryBinder instance is intended to return. + * + *

    This method allows the developer to interrogate this binder's intention + * which may be different from the {@link ILoggerFactory} instance it is able to + * yield in practice. The discrepancy should only occur in case of errors. + * + * @return the class name of the intended {@link ILoggerFactory} instance + */ + public String getLoggerFactoryClassStr(); +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/MDCAdapter.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/MDCAdapter.java new file mode 100644 index 000000000..e3bb47ac1 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/MDCAdapter.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.spi; + +import java.util.Map; + +/** + * This interface abstracts the service offered by various MDC + * implementations. + * + * @author Ceki Gülcü + * @since 1.4.1 + */ +public interface MDCAdapter { + + /** + * Put a context value (the val parameter) as identified with + * the key parameter into the current thread's context map. + * The key parameter cannot be null. The val parameter + * can be null only if the underlying implementation supports it. + * + *

    If the current thread does not have a context map it is created as a side + * effect of this call. + */ + public void put(String key, String val); + + /** + * Get the context identified by the key parameter. + * The key parameter cannot be null. + * + * @return the string value identified by the key parameter. + */ + public String get(String key); + + /** + * Remove the the context identified by the key parameter. + * The key parameter cannot be null. + * + *

    + * This method does nothing if there is no previous value + * associated with key. + */ + public void remove(String key); + + /** + * Clear all entries in the MDC. + */ + public void clear(); + + /** + * Return a copy of the current thread's context map, with keys and + * values of type String. Returned value may be null. + * + * @return A copy of the current thread's context map. May be null. + * @since 1.5.1 + */ + public Map getCopyOfContextMap(); + + /** + * Set the current thread's context map by first clearing any existing + * map and then copying the map passed as parameter. The context map + * parameter must only contain keys and values of type String. + * + * @param contextMap must contain only keys and values of type String + * + * @since 1.5.1 + */ + public void setContextMap(Map contextMap); +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/MarkerFactoryBinder.java b/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/MarkerFactoryBinder.java new file mode 100644 index 000000000..ce1cc3191 --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/MarkerFactoryBinder.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.spi; + +import org.slf4j.IMarkerFactory; + +/** + * An internal interface which helps the static {@link org.slf4j.MarkerFactory} + * class bind with the appropriate {@link IMarkerFactory} instance. + * + * @author Ceki Gülcü + */ +public interface MarkerFactoryBinder { + + /** + * Return the instance of {@link IMarkerFactory} that + * {@link org.slf4j.MarkerFactory} class should bind to. + * + * @return the instance of {@link IMarkerFactory} that + * {@link org.slf4j.MarkerFactory} class should bind to. + */ + public IMarkerFactory getMarkerFactory(); + + /** + * The String form of the {@link IMarkerFactory} object that this + * MarkerFactoryBinder instance is intended to return. + * + *

    This method allows the developer to interrogate this binder's intention + * which may be different from the {@link IMarkerFactory} instance it is able to + * return. Such a discrepancy should only occur in case of errors. + * + * @return the class name of the intended {@link IMarkerFactory} instance + */ + public String getMarkerFactoryClassStr(); +} diff --git a/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/package.html b/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/package.html new file mode 100644 index 000000000..13ea8923a --- /dev/null +++ b/fine-third-default/fine-slf4j-api/src/org/slf4j/spi/package.html @@ -0,0 +1,8 @@ + + + + +Classes and interfaces which are internal to SLF4J. Under most +circumstances SLF4J users should be oblivious even to the existence of +this package. + \ No newline at end of file diff --git a/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/BASE64Decoder.java b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/BASE64Decoder.java new file mode 100644 index 000000000..8c261d23d --- /dev/null +++ b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/BASE64Decoder.java @@ -0,0 +1,96 @@ +package com.fr.third.sun.misc; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PushbackInputStream; + +/** + * @author zhouping + * @version 10.0 + * Created by zhouping on 2019/10/28 + */ +public class BASE64Decoder extends CharacterDecoder { + private static final char[] pem_array = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + private static final byte[] pem_convert_array = new byte[256]; + byte[] decode_buffer = new byte[4]; + + public BASE64Decoder() { + } + + protected int bytesPerAtom() { + return 4; + } + + protected int bytesPerLine() { + return 72; + } + + protected void decodeAtom(PushbackInputStream var1, OutputStream var2, int var3) throws IOException { + byte var5 = -1; + byte var6 = -1; + byte var7 = -1; + byte var8 = -1; + if (var3 < 2) { + throw new CEFormatException("BASE64Decoder: Not enough bytes for an atom."); + } else { + int var4; + do { + var4 = var1.read(); + if (var4 == -1) { + throw new CEStreamExhausted(); + } + } while(var4 == 10 || var4 == 13); + + this.decode_buffer[0] = (byte)var4; + var4 = this.readFully(var1, this.decode_buffer, 1, var3 - 1); + if (var4 == -1) { + throw new CEStreamExhausted(); + } else { + if (var3 > 3 && this.decode_buffer[3] == 61) { + var3 = 3; + } + + if (var3 > 2 && this.decode_buffer[2] == 61) { + var3 = 2; + } + + switch(var3) { + case 4: + var8 = pem_convert_array[this.decode_buffer[3] & 255]; + case 3: + var7 = pem_convert_array[this.decode_buffer[2] & 255]; + case 2: + var6 = pem_convert_array[this.decode_buffer[1] & 255]; + var5 = pem_convert_array[this.decode_buffer[0] & 255]; + default: + switch(var3) { + case 2: + var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); + break; + case 3: + var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); + var2.write((byte)(var6 << 4 & 240 | var7 >>> 2 & 15)); + break; + case 4: + var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); + var2.write((byte)(var6 << 4 & 240 | var7 >>> 2 & 15)); + var2.write((byte)(var7 << 6 & 192 | var8 & 63)); + } + + } + } + } + } + + static { + int var0; + for(var0 = 0; var0 < 255; ++var0) { + pem_convert_array[var0] = -1; + } + + for(var0 = 0; var0 < pem_array.length; ++var0) { + pem_convert_array[pem_array[var0]] = (byte)var0; + } + + } +} \ No newline at end of file diff --git a/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/BASE64Encoder.java b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/BASE64Encoder.java new file mode 100644 index 000000000..8942cc796 --- /dev/null +++ b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/BASE64Encoder.java @@ -0,0 +1,62 @@ +package com.fr.third.sun.misc; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * @author zhouping + * @version 10.0 + * Created by zhouping on 2019/10/28 + */ +public class BASE64Encoder extends CharacterEncoder { + + private static final char[] pem_array = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + + public BASE64Encoder() { + + } + + protected int bytesPerAtom() { + + return 3; + } + + protected int bytesPerLine() { + + return 57; + } + + protected void encodeAtom(OutputStream var1, byte[] var2, int var3, int var4) throws IOException { + + byte var5; + if (var4 == 1) { + var5 = var2[var3]; + byte var6 = 0; + boolean var7 = false; + var1.write(pem_array[var5 >>> 2 & 63]); + var1.write(pem_array[(var5 << 4 & 48) + (var6 >>> 4 & 15)]); + var1.write(61); + var1.write(61); + } else { + byte var8; + if (var4 == 2) { + var5 = var2[var3]; + var8 = var2[var3 + 1]; + byte var9 = 0; + var1.write(pem_array[var5 >>> 2 & 63]); + var1.write(pem_array[(var5 << 4 & 48) + (var8 >>> 4 & 15)]); + var1.write(pem_array[(var8 << 2 & 60) + (var9 >>> 6 & 3)]); + var1.write(61); + } else { + var5 = var2[var3]; + var8 = var2[var3 + 1]; + byte var10 = var2[var3 + 2]; + var1.write(pem_array[var5 >>> 2 & 63]); + var1.write(pem_array[(var5 << 4 & 48) + (var8 >>> 4 & 15)]); + var1.write(pem_array[(var8 << 2 & 60) + (var10 >>> 6 & 3)]); + var1.write(pem_array[var10 & 63]); + } + } + + } +} \ No newline at end of file diff --git a/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CEFormatException.java b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CEFormatException.java new file mode 100644 index 000000000..947c6bf8a --- /dev/null +++ b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CEFormatException.java @@ -0,0 +1,16 @@ +package com.fr.third.sun.misc; + +import java.io.IOException; + +/** + * @author zhouping + * @version 10.0 + * Created by zhouping on 2019/10/28 + */ +public class CEFormatException extends IOException { + + public CEFormatException(String var1) { + + super(var1); + } +} \ No newline at end of file diff --git a/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CEStreamExhausted.java b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CEStreamExhausted.java new file mode 100644 index 000000000..c02f142dd --- /dev/null +++ b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CEStreamExhausted.java @@ -0,0 +1,15 @@ +package com.fr.third.sun.misc; + +import java.io.IOException; + +/** + * @author zhouping + * @version 10.0 + * Created by zhouping on 2019/10/28 + */ +public class CEStreamExhausted extends IOException { + + public CEStreamExhausted() { + + } +} \ No newline at end of file diff --git a/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CharacterDecoder.java b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CharacterDecoder.java new file mode 100644 index 000000000..fe8d50066 --- /dev/null +++ b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CharacterDecoder.java @@ -0,0 +1,107 @@ +package com.fr.third.sun.misc; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.nio.ByteBuffer; + +/** + * @author zhouping + * @version 10.0 + * Created by zhouping on 2019/10/28 + */ +public abstract class CharacterDecoder { + public CharacterDecoder() { + } + + protected abstract int bytesPerAtom(); + + protected abstract int bytesPerLine(); + + protected void decodeBufferPrefix(PushbackInputStream var1, OutputStream var2) throws IOException { + } + + protected void decodeBufferSuffix(PushbackInputStream var1, OutputStream var2) throws IOException { + } + + protected int decodeLinePrefix(PushbackInputStream var1, OutputStream var2) throws IOException { + return this.bytesPerLine(); + } + + protected void decodeLineSuffix(PushbackInputStream var1, OutputStream var2) throws IOException { + } + + protected void decodeAtom(PushbackInputStream var1, OutputStream var2, int var3) throws IOException { + throw new CEStreamExhausted(); + } + + protected int readFully(InputStream var1, byte[] var2, int var3, int var4) throws IOException { + for(int var5 = 0; var5 < var4; ++var5) { + int var6 = var1.read(); + if (var6 == -1) { + return var5 == 0 ? -1 : var5; + } + + var2[var5 + var3] = (byte)var6; + } + + return var4; + } + + public void decodeBuffer(InputStream var1, OutputStream var2) throws IOException { + int var4 = 0; + PushbackInputStream var5 = new PushbackInputStream(var1); + this.decodeBufferPrefix(var5, var2); + + while(true) { + try { + int var6 = this.decodeLinePrefix(var5, var2); + + int var3; + for(var3 = 0; var3 + this.bytesPerAtom() < var6; var3 += this.bytesPerAtom()) { + this.decodeAtom(var5, var2, this.bytesPerAtom()); + var4 += this.bytesPerAtom(); + } + + if (var3 + this.bytesPerAtom() == var6) { + this.decodeAtom(var5, var2, this.bytesPerAtom()); + var4 += this.bytesPerAtom(); + } else { + this.decodeAtom(var5, var2, var6 - var3); + var4 += var6 - var3; + } + + this.decodeLineSuffix(var5, var2); + } catch (CEStreamExhausted var8) { + this.decodeBufferSuffix(var5, var2); + return; + } + } + } + + public byte[] decodeBuffer(String var1) throws IOException { + byte[] var2 = new byte[var1.length()]; + var1.getBytes(0, var1.length(), var2, 0); + ByteArrayInputStream var3 = new ByteArrayInputStream(var2); + ByteArrayOutputStream var4 = new ByteArrayOutputStream(); + this.decodeBuffer(var3, var4); + return var4.toByteArray(); + } + + public byte[] decodeBuffer(InputStream var1) throws IOException { + ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + this.decodeBuffer(var1, var2); + return var2.toByteArray(); + } + + public ByteBuffer decodeBufferToByteBuffer(String var1) throws IOException { + return ByteBuffer.wrap(this.decodeBuffer(var1)); + } + + public ByteBuffer decodeBufferToByteBuffer(InputStream var1) throws IOException { + return ByteBuffer.wrap(this.decodeBuffer(var1)); + } +} \ No newline at end of file diff --git a/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CharacterEncoder.java b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CharacterEncoder.java new file mode 100644 index 000000000..41c77ede0 --- /dev/null +++ b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/CharacterEncoder.java @@ -0,0 +1,186 @@ +package com.fr.third.sun.misc; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.ByteBuffer; + +/** + * @author zhouping + * @version 10.0 + * Created by zhouping on 2019/10/28 + */ +public abstract class CharacterEncoder { + protected PrintStream pStream; + + public CharacterEncoder() { + } + + protected abstract int bytesPerAtom(); + + protected abstract int bytesPerLine(); + + protected void encodeBufferPrefix(OutputStream var1) throws IOException { + this.pStream = new PrintStream(var1); + } + + protected void encodeBufferSuffix(OutputStream var1) throws IOException { + } + + protected void encodeLinePrefix(OutputStream var1, int var2) throws IOException { + } + + protected void encodeLineSuffix(OutputStream var1) throws IOException { + this.pStream.println(); + } + + protected abstract void encodeAtom(OutputStream var1, byte[] var2, int var3, int var4) throws IOException; + + protected int readFully(InputStream var1, byte[] var2) throws IOException { + for (int var3 = 0; var3 < var2.length; ++var3) { + int var4 = var1.read(); + if (var4 == -1) { + return var3; + } + + var2[var3] = (byte) var4; + } + + return var2.length; + } + + public void encode(InputStream var1, OutputStream var2) throws IOException { + byte[] var5 = new byte[this.bytesPerLine()]; + this.encodeBufferPrefix(var2); + + while (true) { + int var4 = this.readFully(var1, var5); + if (var4 == 0) { + break; + } + + this.encodeLinePrefix(var2, var4); + + for (int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { + if (var3 + this.bytesPerAtom() <= var4) { + this.encodeAtom(var2, var5, var3, this.bytesPerAtom()); + } else { + this.encodeAtom(var2, var5, var3, var4 - var3); + } + } + + if (var4 < this.bytesPerLine()) { + break; + } + + this.encodeLineSuffix(var2); + } + + this.encodeBufferSuffix(var2); + } + + public void encode(byte[] var1, OutputStream var2) throws IOException { + ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + this.encode((InputStream) var3, var2); + } + + public String encode(byte[] var1) { + ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + String var4 = null; + + try { + this.encode((InputStream) var3, var2); + var4 = var2.toString("8859_1"); + return var4; + } catch (Exception var6) { + throw new Error("CharacterEncoder.encode internal error"); + } + } + + private byte[] getBytes(ByteBuffer var1) { + byte[] var2 = null; + if (var1.hasArray()) { + byte[] var3 = var1.array(); + if (var3.length == var1.capacity() && var3.length == var1.remaining()) { + var2 = var3; + var1.position(var1.limit()); + } + } + + if (var2 == null) { + var2 = new byte[var1.remaining()]; + var1.get(var2); + } + + return var2; + } + + public void encode(ByteBuffer var1, OutputStream var2) throws IOException { + byte[] var3 = this.getBytes(var1); + this.encode(var3, var2); + } + + public String encode(ByteBuffer var1) { + byte[] var2 = this.getBytes(var1); + return this.encode(var2); + } + + public void encodeBuffer(InputStream var1, OutputStream var2) throws IOException { + byte[] var5 = new byte[this.bytesPerLine()]; + this.encodeBufferPrefix(var2); + + int var4; + do { + var4 = this.readFully(var1, var5); + if (var4 == 0) { + break; + } + + this.encodeLinePrefix(var2, var4); + + for (int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { + if (var3 + this.bytesPerAtom() <= var4) { + this.encodeAtom(var2, var5, var3, this.bytesPerAtom()); + } else { + this.encodeAtom(var2, var5, var3, var4 - var3); + } + } + + this.encodeLineSuffix(var2); + } while (var4 >= this.bytesPerLine()); + + this.encodeBufferSuffix(var2); + } + + public void encodeBuffer(byte[] var1, OutputStream var2) throws IOException { + ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + this.encodeBuffer((InputStream) var3, var2); + } + + public String encodeBuffer(byte[] var1) { + ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + + try { + this.encodeBuffer((InputStream) var3, var2); + } catch (Exception var5) { + throw new Error("CharacterEncoder.encodeBuffer internal error"); + } + + return var2.toString(); + } + + public void encodeBuffer(ByteBuffer var1, OutputStream var2) throws IOException { + byte[] var3 = this.getBytes(var1); + this.encodeBuffer(var3, var2); + } + + public String encodeBuffer(ByteBuffer var1) { + byte[] var2 = this.getBytes(var1); + return this.encodeBuffer(var2); + } +} \ No newline at end of file diff --git a/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/Cleaner.java b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/Cleaner.java new file mode 100644 index 000000000..75b154185 --- /dev/null +++ b/fine-third-default/fine-sun-misc/src/com/fr/third/sun/misc/Cleaner.java @@ -0,0 +1,84 @@ +package com.fr.third.sun.misc; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * @author zhouping + * @version 10.0 + * Created by zhouping on 2019/10/29 + */ +public class Cleaner extends PhantomReference { + private static final ReferenceQueue dummyQueue = new ReferenceQueue(); + private static Cleaner first = null; + private Cleaner next = null; + private Cleaner prev = null; + private final Runnable thunk; + + private static synchronized Cleaner add(Cleaner var0) { + if (first != null) { + var0.next = first; + first.prev = var0; + } + + first = var0; + return var0; + } + + private static synchronized boolean remove(Cleaner var0) { + if (var0.next == var0) { + return false; + } else { + if (first == var0) { + if (var0.next != null) { + first = var0.next; + } else { + first = var0.prev; + } + } + + if (var0.next != null) { + var0.next.prev = var0.prev; + } + + if (var0.prev != null) { + var0.prev.next = var0.next; + } + + var0.next = var0; + var0.prev = var0; + return true; + } + } + + private Cleaner(Object var1, Runnable var2) { + super(var1, dummyQueue); + this.thunk = var2; + } + + public static Cleaner create(Object var0, Runnable var1) { + return var1 == null ? null : add(new Cleaner(var0, var1)); + } + + public void clean() { + if (remove(this)) { + try { + this.thunk.run(); + } catch (final Throwable var2) { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + if (System.err != null) { + (new Error("Cleaner terminated abnormally", var2)).printStackTrace(); + } + + System.exit(1); + return null; + } + }); + } + + } + } +} diff --git a/fine-third-default/src/com/fr/third/javax/xml/stream/XMLEntityReaderImpl.java b/fine-third-default/src/com/fr/third/javax/xml/stream/XMLEntityReaderImpl.java deleted file mode 100644 index 90649414f..000000000 --- a/fine-third-default/src/com/fr/third/javax/xml/stream/XMLEntityReaderImpl.java +++ /dev/null @@ -1,1133 +0,0 @@ -package com.fr.third.javax.xml.stream; - -import com.fr.third.javax.xml.stream.xerces.xni.parser.*; -import java.util.*; -import com.fr.third.javax.xml.stream.xerces.xni.*; -import com.fr.third.javax.xml.stream.xerces.impl.io.*; -import com.fr.third.javax.xml.stream.xerces.util.*; -import java.io.*; - -import static com.fr.third.javax.xml.stream.xerces.util.XMLChar.isInvalid; - -public class XMLEntityReaderImpl extends XMLEntityReader -{ - protected Entity.ScannedEntity fCurrentEntity; - protected XMLEntityManager fEntityManager; - private static final boolean DEBUG_ENCODINGS = false; - private Vector listeners; - public static final boolean[] validContent; - public static final boolean[] validNames; - private static final boolean DEBUG_BUFFER = false; - private static final boolean DEBUG_SKIP_STRING = false; - protected SymbolTable fSymbolTable; - protected XMLErrorReporter fErrorReporter; - int[] whiteSpaceLookup; - int whiteSpaceLen; - boolean whiteSpaceInfoNeeded; - char[] scannedName; - protected boolean fAllowJavaEncodings; - protected static final String SYMBOL_TABLE = "http://apache.org/xml/properties/internal/symbol-table"; - protected static final String ERROR_REPORTER = "http://apache.org/xml/properties/internal/error-reporter"; - protected static final String ALLOW_JAVA_ENCODINGS = "http://apache.org/xml/features/allow-java-encodings"; - protected PropertyManager fPropertyManager; - boolean isExternal; - - static { - validContent = new boolean[127]; - validNames = new boolean[127]; - for (char i = ' '; i < '\u007f'; ++i) { - XMLEntityReaderImpl.validContent[i] = true; - } - XMLEntityReaderImpl.validContent[9] = true; - XMLEntityReaderImpl.validContent[38] = false; - XMLEntityReaderImpl.validContent[60] = false; - for (int j = 65; j <= 90; ++j) { - XMLEntityReaderImpl.validNames[j] = true; - } - for (int j = 97; j <= 122; ++j) { - XMLEntityReaderImpl.validNames[j] = true; - } - for (int j = 48; j <= 57; ++j) { - XMLEntityReaderImpl.validNames[j] = true; - } - XMLEntityReaderImpl.validNames[45] = true; - XMLEntityReaderImpl.validNames[46] = true; - XMLEntityReaderImpl.validNames[58] = true; - XMLEntityReaderImpl.validNames[95] = true; - } - - public XMLEntityReaderImpl(final XMLEntityManager entityManager) { - this.fCurrentEntity = null; - this.listeners = new Vector(); - this.fSymbolTable = null; - this.fErrorReporter = null; - this.whiteSpaceLookup = new int[100]; - this.whiteSpaceLen = 0; - this.whiteSpaceInfoNeeded = true; - this.scannedName = null; - this.fPropertyManager = null; - this.isExternal = false; - this.fEntityManager = entityManager; - } - - public XMLEntityReaderImpl(final PropertyManager propertyManager, final XMLEntityManager entityManager) { - this.fCurrentEntity = null; - this.listeners = new Vector(); - this.fSymbolTable = null; - this.fErrorReporter = null; - this.whiteSpaceLookup = new int[100]; - this.whiteSpaceLen = 0; - this.whiteSpaceInfoNeeded = true; - this.scannedName = null; - this.fPropertyManager = null; - this.isExternal = false; - this.fEntityManager = entityManager; - this.reset(propertyManager); - } - - public void reset(final PropertyManager propertyManager) { - this.fSymbolTable = (SymbolTable)propertyManager.getProperty("http://apache.org/xml/properties/internal/symbol-table"); - this.fErrorReporter = (XMLErrorReporter)propertyManager.getProperty("http://apache.org/xml/properties/internal/error-reporter"); - this.fCurrentEntity = null; - this.whiteSpaceLen = 0; - this.whiteSpaceInfoNeeded = true; - this.scannedName = null; - this.listeners.clear(); - } - - public void reset(final XMLComponentManager componentManager) throws XMLConfigurationException { - try { - this.fAllowJavaEncodings = componentManager.getFeature("http://apache.org/xml/features/allow-java-encodings"); - } - catch (XMLConfigurationException e) { - this.fAllowJavaEncodings = false; - } - this.fSymbolTable = (SymbolTable)componentManager.getProperty("http://apache.org/xml/properties/internal/symbol-table"); - this.fErrorReporter = (XMLErrorReporter)componentManager.getProperty("http://apache.org/xml/properties/internal/error-reporter"); - } - - public void setCurrentEntity(final Entity.ScannedEntity scannedEntity) { - this.fCurrentEntity = scannedEntity; - if (this.fCurrentEntity != null) { - this.isExternal = this.fCurrentEntity.isExternal(); - } - } - - public Entity.ScannedEntity getCurrentEntity() { - return this.fCurrentEntity; - } - - public String getBaseSystemId() { - return (this.fCurrentEntity != null && this.fCurrentEntity.entityLocation != null) ? this.fCurrentEntity.entityLocation.getExpandedSystemId() : null; - } - - public int getLineNumber() { - return (this.fCurrentEntity != null) ? this.fCurrentEntity.lineNumber : -1; - } - - public int getColumnNumber() { - return (this.fCurrentEntity != null) ? this.fCurrentEntity.columnNumber : -1; - } - - public int getCharacterOffset() { - return (this.fCurrentEntity != null) ? (this.fCurrentEntity.fTotalCountTillLastLoad + this.fCurrentEntity.position) : -1; - } - - public String getExpandedSystemId() { - return (this.fCurrentEntity != null && this.fCurrentEntity.entityLocation != null) ? this.fCurrentEntity.entityLocation.getExpandedSystemId() : null; - } - - public String getLiteralSystemId() { - return (this.fCurrentEntity != null && this.fCurrentEntity.entityLocation != null) ? this.fCurrentEntity.entityLocation.getLiteralSystemId() : null; - } - - public String getPublicId() { - return (this.fCurrentEntity != null && this.fCurrentEntity.entityLocation != null) ? this.fCurrentEntity.entityLocation.getPublicId() : null; - } - - public void setVersion(final String version) { - this.fCurrentEntity.version = version; - } - - public String getVersion() { - return this.fCurrentEntity.version; - } - - public String getEncoding() { - return this.fCurrentEntity.encoding; - } - - public void setEncoding(final String encoding) throws IOException { - if (this.fCurrentEntity.stream != null && (this.fCurrentEntity.encoding == null || !this.fCurrentEntity.encoding.equals(encoding))) { - if (this.fCurrentEntity.encoding != null && this.fCurrentEntity.encoding.startsWith("UTF-16")) { - final String ENCODING = encoding.toUpperCase(Locale.ENGLISH); - if (ENCODING.equals("UTF-16")) { - return; - } - if (ENCODING.equals("ISO-10646-UCS-4")) { - if (this.fCurrentEntity.encoding.equals("UTF-16BE")) { - this.fCurrentEntity.reader = new UCSReader(this.fCurrentEntity.stream, (short)8); - } - else { - this.fCurrentEntity.reader = new UCSReader(this.fCurrentEntity.stream, (short)4); - } - return; - } - if (ENCODING.equals("ISO-10646-UCS-2")) { - if (this.fCurrentEntity.encoding.equals("UTF-16BE")) { - this.fCurrentEntity.reader = new UCSReader(this.fCurrentEntity.stream, (short)2); - } - else { - this.fCurrentEntity.reader = new UCSReader(this.fCurrentEntity.stream, (short)1); - } - return; - } - } - this.fCurrentEntity.reader = this.createReader(this.fCurrentEntity.stream, encoding, null); - this.fCurrentEntity.encoding = encoding; - } - } - - public boolean isExternal() { - return this.fCurrentEntity.isExternal(); - } - - public int getChar(final int relative) throws IOException { - if (this.arrangeCapacity(relative + 1, false)) { - return this.fCurrentEntity.ch[this.fCurrentEntity.position + relative]; - } - return -1; - } - - public int peekChar() throws IOException { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - } - final int c = this.fCurrentEntity.ch[this.fCurrentEntity.position]; - if (this.isExternal) { - return (c != 13) ? c : 10; - } - return c; - } - - public int scanChar() throws IOException { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - } - int c = this.fCurrentEntity.ch[this.fCurrentEntity.position++]; - if (c == 10 || (c == 13 && this.isExternal)) { - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - ++fCurrentEntity.lineNumber; - this.fCurrentEntity.columnNumber = 1; - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(1); - this.fCurrentEntity.ch[0] = (char)c; - this.load(1, false); - } - if (c == 13 && this.isExternal) { - if (this.fCurrentEntity.ch[this.fCurrentEntity.position++] != '\n') { - final Entity.ScannedEntity fCurrentEntity2 = this.fCurrentEntity; - --fCurrentEntity2.position; - } - c = 10; - } - } - final Entity.ScannedEntity fCurrentEntity3 = this.fCurrentEntity; - ++fCurrentEntity3.columnNumber; - return c; - } - - public String scanNmtoken() throws IOException { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - } - int offset = this.fCurrentEntity.position; - boolean vc = false; - while (true) { - final char c = this.fCurrentEntity.ch[this.fCurrentEntity.position]; - if (c < '\u007f') { - vc = XMLEntityReaderImpl.validNames[c]; - } - else { - vc = XMLChar.isName(c); - } - if (!vc) { - break; - } - if (++this.fCurrentEntity.position != this.fCurrentEntity.count) { - continue; - } - final int length = this.fCurrentEntity.position - offset; - this.invokeListeners(length); - if (length == this.fCurrentEntity.fBufferSize) { - final char[] tmp = new char[this.fCurrentEntity.fBufferSize * 2]; - System.arraycopy(this.fCurrentEntity.ch, offset, tmp, 0, length); - this.fCurrentEntity.ch = tmp; - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - fCurrentEntity.fBufferSize *= 2; - } - else { - System.arraycopy(this.fCurrentEntity.ch, offset, this.fCurrentEntity.ch, 0, length); - } - offset = 0; - if (this.load(length, false)) { - break; - } - } - final int length = this.fCurrentEntity.position - offset; - final Entity.ScannedEntity fCurrentEntity2 = this.fCurrentEntity; - fCurrentEntity2.columnNumber += length; - String symbol = null; - if (length > 0) { - symbol = this.fSymbolTable.addSymbol(this.fCurrentEntity.ch, offset, length); - } - return symbol; - } - - public String scanName() throws IOException { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - } - int offset = this.fCurrentEntity.position; - if (XMLChar.isNameStart(this.fCurrentEntity.ch[offset])) { - if (++this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(1); - this.fCurrentEntity.ch[0] = this.fCurrentEntity.ch[offset]; - offset = 0; - if (this.load(1, false)) { - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - ++fCurrentEntity.columnNumber; - final String symbol = this.fSymbolTable.addSymbol(this.fCurrentEntity.ch, 0, 1); - this.scannedName = this.fSymbolTable.getCharArray(); - return symbol; - } - } - boolean vc = false; - while (true) { - final char c = this.fCurrentEntity.ch[this.fCurrentEntity.position]; - if (c < '\u007f') { - vc = XMLEntityReaderImpl.validNames[c]; - } - else { - vc = XMLChar.isName(c); - } - if (!vc) { - break; - } - if (++this.fCurrentEntity.position != this.fCurrentEntity.count) { - continue; - } - final int length = this.fCurrentEntity.position - offset; - this.invokeListeners(length); - if (length == this.fCurrentEntity.fBufferSize) { - final char[] tmp = new char[this.fCurrentEntity.fBufferSize * 2]; - System.arraycopy(this.fCurrentEntity.ch, offset, tmp, 0, length); - this.fCurrentEntity.ch = tmp; - final Entity.ScannedEntity fCurrentEntity2 = this.fCurrentEntity; - fCurrentEntity2.fBufferSize *= 2; - } - else { - System.arraycopy(this.fCurrentEntity.ch, offset, this.fCurrentEntity.ch, 0, length); - } - offset = 0; - if (this.load(length, false)) { - break; - } - } - } - final int length2 = this.fCurrentEntity.position - offset; - final Entity.ScannedEntity fCurrentEntity3 = this.fCurrentEntity; - fCurrentEntity3.columnNumber += length2; - String symbol2 = null; - if (length2 > 0) { - symbol2 = this.fSymbolTable.addSymbol(this.fCurrentEntity.ch, offset, length2); - this.scannedName = this.fSymbolTable.getCharArray(); - } - return symbol2; - } - - public boolean scanQName(final QName qname) throws IOException { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - } - int offset = this.fCurrentEntity.position; - if (XMLChar.isNameStart(this.fCurrentEntity.ch[offset])) { - if (++this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(1); - this.fCurrentEntity.ch[0] = this.fCurrentEntity.ch[offset]; - offset = 0; - if (this.load(1, false)) { - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - ++fCurrentEntity.columnNumber; - final String name = this.fSymbolTable.addSymbol(this.fCurrentEntity.ch, 0, 1); - qname.setValues(null, name, name, null); - qname.characters = this.fSymbolTable.getCharArray(); - return true; - } - } - int index = -1; - boolean vc = false; - while (true) { - final char c = this.fCurrentEntity.ch[this.fCurrentEntity.position]; - if (c < '\u007f') { - vc = XMLEntityReaderImpl.validNames[c]; - } - else { - vc = XMLChar.isName(c); - } - if (!vc) { - break; - } - if (c == ':') { - if (index != -1) { - break; - } - index = this.fCurrentEntity.position; - } - if (++this.fCurrentEntity.position != this.fCurrentEntity.count) { - continue; - } - final int length = this.fCurrentEntity.position - offset; - this.invokeListeners(length); - if (length == this.fCurrentEntity.fBufferSize) { - final char[] tmp = new char[this.fCurrentEntity.fBufferSize * 2]; - System.arraycopy(this.fCurrentEntity.ch, offset, tmp, 0, length); - this.fCurrentEntity.ch = tmp; - final Entity.ScannedEntity fCurrentEntity2 = this.fCurrentEntity; - fCurrentEntity2.fBufferSize *= 2; - } - else { - System.arraycopy(this.fCurrentEntity.ch, offset, this.fCurrentEntity.ch, 0, length); - } - if (index != -1) { - index -= offset; - } - offset = 0; - if (this.load(length, false)) { - break; - } - } - final int length2 = this.fCurrentEntity.position - offset; - final Entity.ScannedEntity fCurrentEntity3 = this.fCurrentEntity; - fCurrentEntity3.columnNumber += length2; - if (length2 > 0) { - String prefix = null; - String localpart = null; - final String rawname = this.fSymbolTable.addSymbol(this.fCurrentEntity.ch, offset, length2); - qname.characters = this.fSymbolTable.getCharArray(); - if (index != -1) { - final int prefixLength = index - offset; - prefix = this.fSymbolTable.addSymbol(this.fCurrentEntity.ch, offset, prefixLength); - final int len = length2 - prefixLength - 1; - localpart = this.fSymbolTable.addSymbol(this.fCurrentEntity.ch, index + 1, len); - } - else { - localpart = rawname; - } - qname.setValues(prefix, localpart, rawname, null); - return true; - } - } - return false; - } - - public int scanContent(final XMLString content) throws IOException { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - } - else if (this.fCurrentEntity.position == this.fCurrentEntity.count - 1) { - this.invokeListeners(0); - this.fCurrentEntity.ch[0] = this.fCurrentEntity.ch[this.fCurrentEntity.count - 1]; - this.load(1, false); - this.fCurrentEntity.position = 0; - } - int offset = this.fCurrentEntity.position; - int c = this.fCurrentEntity.ch[offset]; - int newlines = 0; - if (c == 10 || (c == 13 && this.isExternal)) { - do { - c = this.fCurrentEntity.ch[this.fCurrentEntity.position++]; - if (c == 13 && this.isExternal) { - ++newlines; - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - ++fCurrentEntity.lineNumber; - this.fCurrentEntity.columnNumber = 1; - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - offset = 0; - this.invokeListeners(newlines); - this.fCurrentEntity.position = newlines; - if (this.load(newlines, false)) { - break; - } - } - if (this.fCurrentEntity.ch[this.fCurrentEntity.position] == '\n') { - final Entity.ScannedEntity fCurrentEntity2 = this.fCurrentEntity; - ++fCurrentEntity2.position; - ++offset; - } - else { - ++newlines; - } - } - else { - if (c != 10) { - final Entity.ScannedEntity fCurrentEntity3 = this.fCurrentEntity; - --fCurrentEntity3.position; - break; - } - ++newlines; - final Entity.ScannedEntity fCurrentEntity4 = this.fCurrentEntity; - ++fCurrentEntity4.lineNumber; - this.fCurrentEntity.columnNumber = 1; - if (this.fCurrentEntity.position != this.fCurrentEntity.count) { - continue; - } - offset = 0; - this.invokeListeners(newlines); - this.fCurrentEntity.position = newlines; - if (this.load(newlines, false)) { - break; - } - continue; - } - } while (this.fCurrentEntity.position < this.fCurrentEntity.count - 1); - for (int i = offset; i < this.fCurrentEntity.position; ++i) { - this.fCurrentEntity.ch[i] = '\n'; - } - final int length = this.fCurrentEntity.position - offset; - if (this.fCurrentEntity.position == this.fCurrentEntity.count - 1) { - content.setValues(this.fCurrentEntity.ch, offset, length); - return -1; - } - } - boolean vc = false; - while (this.fCurrentEntity.position < this.fCurrentEntity.count) { - c = this.fCurrentEntity.ch[this.fCurrentEntity.position++]; - if (c < 127) { - vc = XMLEntityReaderImpl.validContent[c]; - } - else { - vc = XMLChar.isContent(c); - } - if (!vc) { - final Entity.ScannedEntity fCurrentEntity5 = this.fCurrentEntity; - --fCurrentEntity5.position; - break; - } - } - final int length2 = this.fCurrentEntity.position - offset; - final Entity.ScannedEntity fCurrentEntity6 = this.fCurrentEntity; - fCurrentEntity6.columnNumber += length2 - newlines; - content.setValues(this.fCurrentEntity.ch, offset, length2); - if (this.fCurrentEntity.position != this.fCurrentEntity.count) { - c = this.fCurrentEntity.ch[this.fCurrentEntity.position]; - if (c == 13 && this.isExternal) { - c = 10; - } - } - else { - c = -1; - } - return c; - } - - public int scanLiteral(final int quote, final XMLString content) throws IOException { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - } - else if (this.fCurrentEntity.position == this.fCurrentEntity.count - 1) { - this.invokeListeners(0); - this.fCurrentEntity.ch[0] = this.fCurrentEntity.ch[this.fCurrentEntity.count - 1]; - this.load(1, false); - this.fCurrentEntity.position = 0; - } - int offset = this.fCurrentEntity.position; - int c = this.fCurrentEntity.ch[offset]; - int newlines = 0; - if (this.whiteSpaceInfoNeeded) { - this.whiteSpaceLen = 0; - } - if (c == 10 || (c == 13 && this.isExternal)) { - do { - c = this.fCurrentEntity.ch[this.fCurrentEntity.position++]; - if (c == 13 && this.isExternal) { - ++newlines; - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - ++fCurrentEntity.lineNumber; - this.fCurrentEntity.columnNumber = 1; - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(newlines); - offset = 0; - this.fCurrentEntity.position = newlines; - if (this.load(newlines, false)) { - break; - } - } - if (this.fCurrentEntity.ch[this.fCurrentEntity.position] == '\n') { - final Entity.ScannedEntity fCurrentEntity2 = this.fCurrentEntity; - ++fCurrentEntity2.position; - ++offset; - } - else { - ++newlines; - } - } - else { - if (c != 10) { - final Entity.ScannedEntity fCurrentEntity3 = this.fCurrentEntity; - --fCurrentEntity3.position; - break; - } - ++newlines; - final Entity.ScannedEntity fCurrentEntity4 = this.fCurrentEntity; - ++fCurrentEntity4.lineNumber; - this.fCurrentEntity.columnNumber = 1; - if (this.fCurrentEntity.position != this.fCurrentEntity.count) { - continue; - } - offset = 0; - this.invokeListeners(newlines); - this.fCurrentEntity.position = newlines; - if (this.load(newlines, false)) { - break; - } - continue; - } - } while (this.fCurrentEntity.position < this.fCurrentEntity.count - 1); - int i; - for (i = 0, i = offset; i < this.fCurrentEntity.position; ++i) { - this.fCurrentEntity.ch[i] = '\n'; - this.whiteSpaceLookup[this.whiteSpaceLen++] = i; - } - final int length = this.fCurrentEntity.position - offset; - if (this.fCurrentEntity.position == this.fCurrentEntity.count - 1) { - content.setValues(this.fCurrentEntity.ch, offset, length); - return -1; - } - } - boolean vc = true; - while (this.fCurrentEntity.position < this.fCurrentEntity.count) { - c = this.fCurrentEntity.ch[this.fCurrentEntity.position++]; - if ((c == quote && (!this.fCurrentEntity.literal || this.isExternal)) || c == 37) { - final Entity.ScannedEntity fCurrentEntity5 = this.fCurrentEntity; - --fCurrentEntity5.position; - break; - } - if (c < 127) { - vc = XMLEntityReaderImpl.validContent[c]; - } - else { - vc = XMLChar.isContent(c); - } - if (!vc) { - final Entity.ScannedEntity fCurrentEntity6 = this.fCurrentEntity; - --fCurrentEntity6.position; - break; - } - if (!this.whiteSpaceInfoNeeded || (c != 32 && c != 9)) { - continue; - } - if (this.whiteSpaceLen < this.whiteSpaceLookup.length) { - this.whiteSpaceLookup[this.whiteSpaceLen++] = this.fCurrentEntity.position - 1; - } - else { - final int[] tmp = new int[this.whiteSpaceLookup.length + 20]; - System.arraycopy(this.whiteSpaceLookup, 0, tmp, 0, this.whiteSpaceLookup.length); - (this.whiteSpaceLookup = tmp)[this.whiteSpaceLen++] = this.fCurrentEntity.position - 1; - } - } - final int length = this.fCurrentEntity.position - offset; - final Entity.ScannedEntity fCurrentEntity7 = this.fCurrentEntity; - fCurrentEntity7.columnNumber += length - newlines; - content.setValues(this.fCurrentEntity.ch, offset, length); - if (this.fCurrentEntity.position != this.fCurrentEntity.count) { - c = this.fCurrentEntity.ch[this.fCurrentEntity.position]; - if (c == quote && this.fCurrentEntity.literal) { - c = -1; - } - } - else { - c = -1; - } - return c; - } - - public boolean scanData(final String delimiter, final XMLStringBuffer buffer) throws IOException { - boolean done = false; - final int delimLen = delimiter.length(); - final char charAt0 = delimiter.charAt(0); - do { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - } - else if (this.fCurrentEntity.position >= this.fCurrentEntity.count - delimLen) { - this.invokeListeners(this.fCurrentEntity.count - this.fCurrentEntity.position); - System.arraycopy(this.fCurrentEntity.ch, this.fCurrentEntity.position, this.fCurrentEntity.ch, 0, this.fCurrentEntity.count - this.fCurrentEntity.position); - this.load(this.fCurrentEntity.count - this.fCurrentEntity.position, false); - this.fCurrentEntity.position = 0; - } - if (this.fCurrentEntity.position >= this.fCurrentEntity.count - delimLen) { - this.invokeListeners(0); - final int length = this.fCurrentEntity.count - this.fCurrentEntity.position; - buffer.append(this.fCurrentEntity.ch, this.fCurrentEntity.position, length); - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - fCurrentEntity.columnNumber += this.fCurrentEntity.count; - this.fCurrentEntity.position = this.fCurrentEntity.count; - this.load(0, true); - return false; - } - int offset = this.fCurrentEntity.position; - int c = this.fCurrentEntity.ch[offset]; - int newlines = 0; - if (c == 10 || (c == 13 && this.isExternal)) { - do { - c = this.fCurrentEntity.ch[this.fCurrentEntity.position++]; - if (c == 13 && this.isExternal) { - ++newlines; - final Entity.ScannedEntity fCurrentEntity2 = this.fCurrentEntity; - ++fCurrentEntity2.lineNumber; - this.fCurrentEntity.columnNumber = 1; - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - offset = 0; - this.invokeListeners(newlines); - this.fCurrentEntity.position = newlines; - if (this.load(newlines, false)) { - break; - } - } - if (this.fCurrentEntity.ch[this.fCurrentEntity.position] == '\n') { - final Entity.ScannedEntity fCurrentEntity3 = this.fCurrentEntity; - ++fCurrentEntity3.position; - ++offset; - } - else { - ++newlines; - } - } - else { - if (c != 10) { - final Entity.ScannedEntity fCurrentEntity4 = this.fCurrentEntity; - --fCurrentEntity4.position; - break; - } - ++newlines; - final Entity.ScannedEntity fCurrentEntity5 = this.fCurrentEntity; - ++fCurrentEntity5.lineNumber; - this.fCurrentEntity.columnNumber = 1; - if (this.fCurrentEntity.position != this.fCurrentEntity.count) { - continue; - } - offset = 0; - this.invokeListeners(newlines); - this.fCurrentEntity.position = newlines; - this.fCurrentEntity.count = newlines; - if (this.load(newlines, false)) { - break; - } - continue; - } - } while (this.fCurrentEntity.position < this.fCurrentEntity.count - 1); - for (int i = offset; i < this.fCurrentEntity.position; ++i) { - this.fCurrentEntity.ch[i] = '\n'; - } - final int length2 = this.fCurrentEntity.position - offset; - if (this.fCurrentEntity.position == this.fCurrentEntity.count - 1) { - buffer.append(this.fCurrentEntity.ch, offset, length2); - return true; - } - } - Label_0949: - while (this.fCurrentEntity.position < this.fCurrentEntity.count) { - c = this.fCurrentEntity.ch[this.fCurrentEntity.position++]; - if (c == charAt0) { - final int delimOffset = this.fCurrentEntity.position - 1; - for (int j = 1; j < delimLen; ++j) { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - final Entity.ScannedEntity fCurrentEntity6 = this.fCurrentEntity; - fCurrentEntity6.position -= j; - break Label_0949; - } - c = this.fCurrentEntity.ch[this.fCurrentEntity.position++]; - if (delimiter.charAt(j) != c) { - final Entity.ScannedEntity fCurrentEntity7 = this.fCurrentEntity; - fCurrentEntity7.position -= j; - break; - } - } - if (this.fCurrentEntity.position == delimOffset + delimLen) { - done = true; - break; - } - } - else { - if (c == 10 || (this.isExternal && c == 13)) { - final Entity.ScannedEntity fCurrentEntity8 = this.fCurrentEntity; - --fCurrentEntity8.position; - break; - } - if (isInvalid(c)) { - if(XMLChar.isHighSurrogate(c)){ - this.fCurrentEntity.position--; - if (this.fCurrentEntity.position - offset > 0){ - int length2 = this.fCurrentEntity.position - offset; - final Entity.ScannedEntity fCurrentEntity10 = this.fCurrentEntity; - fCurrentEntity10.columnNumber += length2 - newlines; - buffer.append(this.fCurrentEntity.ch, offset, length2); - } - final int high = this.scanChar(); - final int low = this.peekChar(); - if (!XMLChar.isLowSurrogate(low)) { - return false; - } - int x = XMLChar.supplemental((char)high, (char)low); - if (isInvalid(x)) { - return false; - } - buffer.append((char)high); - buffer.append((char)low); - offset = ++this.fCurrentEntity.position; - break; - } - final Entity.ScannedEntity fCurrentEntity9 = this.fCurrentEntity; - --fCurrentEntity9.position; - final int length2 = this.fCurrentEntity.position - offset; - final Entity.ScannedEntity fCurrentEntity10 = this.fCurrentEntity; - fCurrentEntity10.columnNumber += length2 - newlines; - buffer.append(this.fCurrentEntity.ch, offset, length2); - return true; - } - continue; - } - } - int length2 = this.fCurrentEntity.position - offset; - if (length2 != 0){ - final Entity.ScannedEntity fCurrentEntity11 = this.fCurrentEntity; - fCurrentEntity11.columnNumber += length2 - newlines; - if (done) { - length2 -= delimLen; - } - buffer.append(this.fCurrentEntity.ch, offset, length2); - } - } while (!done); - return !done; - } - - public boolean skipChar(final int c) throws IOException { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - } - final int cc = this.fCurrentEntity.ch[this.fCurrentEntity.position]; - if (cc == c) { - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - ++fCurrentEntity.position; - if (c == 10) { - final Entity.ScannedEntity fCurrentEntity2 = this.fCurrentEntity; - ++fCurrentEntity2.lineNumber; - this.fCurrentEntity.columnNumber = 1; - } - else { - final Entity.ScannedEntity fCurrentEntity3 = this.fCurrentEntity; - ++fCurrentEntity3.columnNumber; - } - return true; - } - if (c == 10 && cc == 13 && this.isExternal) { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(1); - this.fCurrentEntity.ch[0] = (char)cc; - this.load(1, false); - } - final Entity.ScannedEntity fCurrentEntity4 = this.fCurrentEntity; - ++fCurrentEntity4.position; - if (this.fCurrentEntity.ch[this.fCurrentEntity.position] == '\n') { - final Entity.ScannedEntity fCurrentEntity5 = this.fCurrentEntity; - ++fCurrentEntity5.position; - } - final Entity.ScannedEntity fCurrentEntity6 = this.fCurrentEntity; - ++fCurrentEntity6.lineNumber; - this.fCurrentEntity.columnNumber = 1; - return true; - } - return false; - } - - public boolean isSpace(final char ch) { - return ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r'; - } - - public boolean skipSpaces() throws IOException { - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - } - if (this.fCurrentEntity == null) { - return false; - } - int c = this.fCurrentEntity.ch[this.fCurrentEntity.position]; - if (XMLChar.isSpace(c)) { - do { - boolean entityChanged = false; - if (c == 10 || (this.isExternal && c == 13)) { - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - ++fCurrentEntity.lineNumber; - this.fCurrentEntity.columnNumber = 1; - if (this.fCurrentEntity.position == this.fCurrentEntity.count - 1) { - this.invokeListeners(0); - this.fCurrentEntity.ch[0] = (char)c; - entityChanged = this.load(1, true); - if (!entityChanged) { - this.fCurrentEntity.position = 0; - } - else if (this.fCurrentEntity == null) { - return true; - } - } - if (c == 13 && this.isExternal && this.fCurrentEntity.ch[++this.fCurrentEntity.position] != '\n') { - final Entity.ScannedEntity fCurrentEntity2 = this.fCurrentEntity; - --fCurrentEntity2.position; - } - } - else { - final Entity.ScannedEntity fCurrentEntity3 = this.fCurrentEntity; - ++fCurrentEntity3.columnNumber; - } - if (!entityChanged) { - final Entity.ScannedEntity fCurrentEntity4 = this.fCurrentEntity; - ++fCurrentEntity4.position; - } - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.invokeListeners(0); - this.load(0, true); - if (this.fCurrentEntity == null) { - return true; - } - continue; - } - } while (XMLChar.isSpace(c = this.fCurrentEntity.ch[this.fCurrentEntity.position])); - return true; - } - return false; - } - - public boolean arrangeCapacity(final int length) throws IOException { - return this.arrangeCapacity(length, false); - } - - public boolean arrangeCapacity(final int length, final boolean changeEntity) throws IOException { - if (this.fCurrentEntity.count - this.fCurrentEntity.position >= length) { - return true; - } - boolean entityChanged = false; - while (this.fCurrentEntity.count - this.fCurrentEntity.position < length) { - if (this.fCurrentEntity.ch.length - this.fCurrentEntity.position < length) { - this.invokeListeners(0); - System.arraycopy(this.fCurrentEntity.ch, this.fCurrentEntity.position, this.fCurrentEntity.ch, 0, this.fCurrentEntity.count - this.fCurrentEntity.position); - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - fCurrentEntity.count -= this.fCurrentEntity.position; - this.fCurrentEntity.position = 0; - } - if (this.fCurrentEntity.count - this.fCurrentEntity.position < length) { - final int pos = this.fCurrentEntity.position; - this.invokeListeners(pos); - entityChanged = this.load(this.fCurrentEntity.count, changeEntity); - this.fCurrentEntity.position = pos; - if (entityChanged) { - break; - } - continue; - } - } - return this.fCurrentEntity.count - this.fCurrentEntity.position >= length; - } - - public boolean skipString(final String s) throws IOException { - final int length = s.length(); - if (this.arrangeCapacity(length, false)) { - final int beforeSkip = this.fCurrentEntity.position; - int afterSkip = this.fCurrentEntity.position + length - 1; - int i = length - 1; - while (s.charAt(i--) == this.fCurrentEntity.ch[afterSkip]) { - if (afterSkip-- == beforeSkip) { - this.fCurrentEntity.position += length; - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - fCurrentEntity.columnNumber += length; - return true; - } - } - } - return false; - } - - public boolean skipString(final char[] s) throws IOException { - final int length = s.length; - if (this.arrangeCapacity(length, false)) { - int beforeSkip = this.fCurrentEntity.position; - final int afterSkip = this.fCurrentEntity.position + length; - for (int i = 0; i < length; ++i) { - if (this.fCurrentEntity.ch[beforeSkip++] != s[i]) { - return false; - } - } - this.fCurrentEntity.position += length; - final Entity.ScannedEntity fCurrentEntity = this.fCurrentEntity; - fCurrentEntity.columnNumber += length; - return true; - } - return false; - } - - final boolean load(final int offset, final boolean changeEntity) throws IOException { - this.fCurrentEntity.fTotalCountTillLastLoad += this.fCurrentEntity.fLastCount; - final int length = this.fCurrentEntity.mayReadChunks ? (this.fCurrentEntity.ch.length - offset) : 64; - final int count = this.fCurrentEntity.reader.read(this.fCurrentEntity.ch, offset, length); - boolean entityChanged = false; - if (count != -1) { - if (count != 0) { - this.fCurrentEntity.fLastCount = count; - this.fCurrentEntity.count = count + offset; - this.fCurrentEntity.position = offset; - } - } - else { - this.fCurrentEntity.count = offset; - this.fCurrentEntity.position = offset; - entityChanged = true; - if (changeEntity) { - this.fEntityManager.endEntity(); - if (this.fCurrentEntity == null) { - return true; - } - if (this.fCurrentEntity.position == this.fCurrentEntity.count) { - this.load(0, true); - } - } - } - return entityChanged; - } - - protected Reader createReader(final InputStream inputStream, String encoding, final Boolean isBigEndian) throws IOException { - if (encoding == null) { - encoding = "UTF-8"; - } - final String ENCODING = encoding.toUpperCase(Locale.ENGLISH); - if (ENCODING.equals("UTF-8")) { - return new UTF8Reader(inputStream, this.fCurrentEntity.fBufferSize, this.fErrorReporter.getMessageFormatter("http://www.w3.org/TR/1998/REC-xml-19980210"), this.fErrorReporter.getLocale()); - } - if (ENCODING.equals("US-ASCII")) { - return new ASCIIReader(inputStream, this.fCurrentEntity.fBufferSize, this.fErrorReporter.getMessageFormatter("http://www.w3.org/TR/1998/REC-xml-19980210"), this.fErrorReporter.getLocale()); - } - if (ENCODING.equals("ISO-10646-UCS-4")) { - if (isBigEndian != null) { - final boolean isBE = isBigEndian; - if (isBE) { - return new UCSReader(inputStream, (short)8); - } - return new UCSReader(inputStream, (short)4); - } - else { - this.fErrorReporter.reportError("http://www.w3.org/TR/1998/REC-xml-19980210", "EncodingByteOrderUnsupported", new Object[] { encoding }, (short)2); - } - } - if (ENCODING.equals("ISO-10646-UCS-2")) { - if (isBigEndian != null) { - final boolean isBE = isBigEndian; - if (isBE) { - return new UCSReader(inputStream, (short)2); - } - return new UCSReader(inputStream, (short)1); - } - else { - this.fErrorReporter.reportError("http://www.w3.org/TR/1998/REC-xml-19980210", "EncodingByteOrderUnsupported", new Object[] { encoding }, (short)2); - } - } - final boolean validIANA = XMLChar.isValidIANAEncoding(encoding); - final boolean validJava = XMLChar.isValidJavaEncoding(encoding); - if (!validIANA || (this.fAllowJavaEncodings && !validJava)) { - this.fErrorReporter.reportError("http://www.w3.org/TR/1998/REC-xml-19980210", "EncodingDeclInvalid", new Object[] { encoding }, (short)2); - encoding = "ISO-8859-1"; - } - String javaEncoding = EncodingMap.getIANA2JavaMapping(ENCODING); - if (javaEncoding == null) { - if (this.fAllowJavaEncodings) { - javaEncoding = encoding; - } - else { - this.fErrorReporter.reportError("http://www.w3.org/TR/1998/REC-xml-19980210", "EncodingDeclInvalid", new Object[] { encoding }, (short)2); - javaEncoding = "ISO8859_1"; - } - } - return new InputStreamReader(inputStream, javaEncoding); - } - - protected Object[] getEncodingName(final byte[] b4, final int count) { - if (count < 2) { - return new Object[] { "UTF-8", null }; - } - final int b5 = b4[0] & 0xFF; - final int b6 = b4[1] & 0xFF; - if (b5 == 254 && b6 == 255) { - return new Object[] { "UTF-16BE", new Boolean(true) }; - } - if (b5 == 255 && b6 == 254) { - return new Object[] { "UTF-16LE", new Boolean(false) }; - } - if (count < 3) { - return new Object[] { "UTF-8", null }; - } - final int b7 = b4[2] & 0xFF; - if (b5 == 239 && b6 == 187 && b7 == 191) { - return new Object[] { "UTF-8", null }; - } - if (count < 4) { - return new Object[] { "UTF-8", null }; - } - final int b8 = b4[3] & 0xFF; - if (b5 == 0 && b6 == 0 && b7 == 0 && b8 == 60) { - return new Object[] { "ISO-10646-UCS-4", new Boolean(true) }; - } - if (b5 == 60 && b6 == 0 && b7 == 0 && b8 == 0) { - return new Object[] { "ISO-10646-UCS-4", new Boolean(false) }; - } - if (b5 == 0 && b6 == 0 && b7 == 60 && b8 == 0) { - return new Object[] { "ISO-10646-UCS-4", null }; - } - if (b5 == 0 && b6 == 60 && b7 == 0 && b8 == 0) { - return new Object[] { "ISO-10646-UCS-4", null }; - } - if (b5 == 0 && b6 == 60 && b7 == 0 && b8 == 63) { - return new Object[] { "UTF-16BE", new Boolean(true) }; - } - if (b5 == 60 && b6 == 0 && b7 == 63 && b8 == 0) { - return new Object[] { "UTF-16LE", new Boolean(false) }; - } - if (b5 == 76 && b6 == 111 && b7 == 167 && b8 == 148) { - return new Object[] { "CP037", null }; - } - return new Object[] { "UTF-8", null }; - } - - final void print() { - } - - public void registerListener(final XMLBufferListener listener) { - if (!this.listeners.contains(listener)) { - this.listeners.add(listener); - } - } - - private void invokeListeners(final int loadPos) { - for (int i = 0; i < this.listeners.size(); ++i) { - final XMLBufferListener listener = (XMLBufferListener) this.listeners.get(i); - listener.refresh(loadPos); - } - } -}