@@ -18,9 +18,9 @@
*/
package org.apache.deltaspike.core.impl.config;

import org.apache.deltaspike.core.util.ClassUtils;
import org.apache.deltaspike.core.spi.config.ConfigSource;
import org.apache.deltaspike.core.spi.config.ConfigSourceProvider;
import org.apache.deltaspike.core.util.PropertyFileUtils;

import java.io.IOException;
import java.net.URL;
@@ -52,17 +52,9 @@ public DefaultConfigSourceProvider()
this.configSources.add(new EnvironmentPropertyConfigSource());
this.configSources.add(new LocalJndiConfigSource());

ClassLoader cl = ClassUtils.getClassLoader(this);
try
try
{
Enumeration<URL> propertyFileUrls = cl.getResources(PROPERTY_FILE_NAME);

//fallback - see DELTASPIKE-98
if (!propertyFileUrls.hasMoreElements())
{
cl = getClass().getClassLoader();
propertyFileUrls = cl.getResources(PROPERTY_FILE_NAME);
}
Enumeration<URL> propertyFileUrls = PropertyFileUtils.resolvePropertyFiles(PROPERTY_FILE_NAME);

while (propertyFileUrls.hasMoreElements())
{
@@ -18,8 +18,8 @@
*/
package org.apache.deltaspike.core.impl.config;

import java.io.IOException;
import java.io.InputStream;
import org.apache.deltaspike.core.util.PropertyFileUtils;

import java.net.URL;
import java.util.Properties;

@@ -35,7 +35,7 @@ class PropertyFileConfigSource extends BaseConfigSource
PropertyFileConfigSource(URL propertyFileUrl)
{
fileName = propertyFileUrl.toExternalForm();
properties = loadProperties(propertyFileUrl);
properties = PropertyFileUtils.loadProperties(propertyFileUrl);
initOrdinal(100);
}

@@ -59,42 +59,4 @@ public String getConfigName()
{
return fileName;
}



private Properties loadProperties(URL url)
{
Properties props = new Properties();

InputStream inputStream = null;
try
{
inputStream = url.openStream();

if (inputStream != null)
{
props.load(inputStream);
}
}
catch (IOException e)
{
throw new IllegalStateException(e);
}
finally
{
try
{
if (inputStream != null)
{
inputStream.close();
}
}
catch (IOException e)
{
// no worries, means that the file is already closed
}
}

return props;
}
}
@@ -0,0 +1,70 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.deltaspike.core.impl.message;

import org.apache.deltaspike.core.api.message.MessageResolver;
import org.apache.deltaspike.core.util.PropertyFileUtils;

import javax.enterprise.inject.Typed;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

@Typed()
class DefaultMessageResolver implements MessageResolver
{
private static final long serialVersionUID = 5834411208472341006L;

private final ResourceBundle messageBundle;

DefaultMessageResolver(String messageBundleName, Locale locale)
{
ResourceBundle resolvedBundle;
try
{
resolvedBundle = PropertyFileUtils.getResourceBundle(messageBundleName, locale);
}
catch (MissingResourceException e)
{
//X TODO log it
resolvedBundle = null;
}

this.messageBundle = resolvedBundle;
}

@Override
public String getMessage(String messageDescriptor)
{
if (this.messageBundle != null && messageDescriptor != null &&
messageDescriptor.startsWith("{") && messageDescriptor.endsWith("}"))
{
try
{
return this.messageBundle.getString(messageDescriptor.substring(1, messageDescriptor.length() - 1));
}
catch (MissingResourceException e)
{
return MISSING_RESOURCE_MARKER + messageDescriptor + MISSING_RESOURCE_MARKER;
}
}

return messageDescriptor;
}
}
@@ -33,7 +33,6 @@
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessProducerMethod;

import org.apache.deltaspike.core.api.literal.MessageBundleLiteral;
import org.apache.deltaspike.core.api.message.MessageBundle;
import org.apache.deltaspike.core.spi.activation.Deactivatable;
import org.apache.deltaspike.core.util.ClassDeactivationUtils;
@@ -104,21 +103,23 @@ protected void captureProducers(AnnotatedMethod<?> method, Bean<?> bean)
}

@SuppressWarnings("UnusedDeclaration")
protected void installBeans(@Observes AfterBeanDiscovery event, BeanManager beanManager)
protected void installMessageBundleProducerBeans(@Observes AfterBeanDiscovery event, BeanManager beanManager)
{
for (AnnotatedType<?> type : messageBundleTypes)
{
event.addBean(createMessageBundleBean(bundleProducerBean, type,
beanManager));
event.addBean(createMessageBundleBean(bundleProducerBean, type, beanManager));
}
}

private static <T> Bean<T> createMessageBundleBean(Bean<Object> delegate,
AnnotatedType<T> type, BeanManager beanManager)
AnnotatedType<T> annotatedType,
BeanManager beanManager)
{
return new NarrowingBeanBuilder<T>(delegate, beanManager)
.readFromType(type).types(type.getBaseType(), Object.class)
.addQualifier(new MessageBundleLiteral()).create();
.readFromType(annotatedType)
//X TODO re-visit type.getBaseType() in combination with #addQualifier
.types(annotatedType.getJavaClass(), Object.class)
.create();
}

@SuppressWarnings("UnusedDeclaration")
@@ -18,11 +18,18 @@
*/
package org.apache.deltaspike.core.impl.message;

import org.apache.deltaspike.core.api.literal.MessageContextConfigLiteral;
import org.apache.deltaspike.core.api.message.LocaleResolver;
import org.apache.deltaspike.core.api.message.Message;
import org.apache.deltaspike.core.api.message.MessageContextConfig;
import org.apache.deltaspike.core.api.message.MessageInterpolator;
import org.apache.deltaspike.core.api.message.MessageResolver;
import org.apache.deltaspike.core.api.provider.BeanProvider;
import org.apache.deltaspike.core.util.ClassUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Locale;

class MessageBundleInvocationHandler implements InvocationHandler
{
@@ -34,28 +41,62 @@ class MessageBundleInvocationHandler implements InvocationHandler
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable
{
final Message message = method.getAnnotation(Message.class);

if (message == null)
{
// nothing to do...
return null;
}

String result;
switch (message.format())
//X TODO discuss use-cases for a deeper lookup and qualifier support
MessageContextConfig messageContextConfig =
method.getDeclaringClass().getAnnotation(MessageContextConfig.class);

if (messageContextConfig == null)
{
case PRINTF:
{
result = String.format(message.value(), args);
break;
}
case MESSAGE_FORMAT:
messageContextConfig = new MessageContextConfigLiteral();
}

String messageTemplate;

if (!MessageResolver.class.equals(messageContextConfig.messageResolver()))
{
Class<? extends MessageResolver> messageResolverClass =
ClassUtils.tryToLoadClassForName(messageContextConfig.messageResolver().getName());

MessageResolver messageResolver = BeanProvider.getContextualReference(messageResolverClass);

messageTemplate = messageResolver.getMessage(message.value());
}
else
{
Class<? extends LocaleResolver> localeResolverClass =
ClassUtils.tryToLoadClassForName(messageContextConfig.localeResolver().getName());

Locale resolvedLocale = Locale.getDefault();

if (!LocaleResolver.class.equals(localeResolverClass))
{
result = MessageFormat.format(message.value(), args);
break;
LocaleResolver localeResolver = BeanProvider.getContextualReference(localeResolverClass);

resolvedLocale = localeResolver.getLocale();
}
default:
throw new IllegalStateException();

String messageBundleName = method.getDeclaringClass().getName();
messageTemplate = new DefaultMessageResolver(messageBundleName, resolvedLocale).getMessage(message.value());
}

Class<? extends MessageInterpolator> messageInterpolatorClass =
ClassUtils.tryToLoadClassForName(messageContextConfig.messageInterpolator().getName());

String result = messageTemplate;

if (!MessageInterpolator.class.equals(messageInterpolatorClass))
{
MessageInterpolator messageInterpolator = BeanProvider.getContextualReference(messageInterpolatorClass);
result = messageInterpolator.interpolate(messageTemplate, args);
}

return result;
}
}
@@ -20,13 +20,10 @@

import javax.inject.Inject;

import org.apache.deltaspike.core.api.message.MessageBundle;

public class Jay
{
@Inject
@MessageBundle
private BirdMessages messages;
private TestMessages messages;

String getMessage()
{
@@ -18,10 +18,6 @@
*/
package org.apache.deltaspike.test.core.api.message;

import static org.junit.Assert.assertEquals;

import javax.enterprise.inject.spi.Extension;

import org.apache.deltaspike.core.api.provider.BeanManagerProvider;
import org.apache.deltaspike.core.impl.message.MessageBundleExtension;
import org.apache.deltaspike.test.util.ArchiveUtils;
@@ -34,12 +30,20 @@
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.enterprise.inject.spi.Extension;
import javax.inject.Inject;

import static org.junit.Assert.assertEquals;

/**
* Tests for {@link org.apache.deltaspike.core.api.message.Message}
*/
@RunWith(Arquillian.class)
public class MessageTest
{
@Inject
private TestMessages messages;

/**
* X TODO creating a WebArchive is only a workaround because JavaArchive
* cannot contain other archives.
@@ -75,4 +79,16 @@ public void testMessageBundleInjection(Jay jay)
{
assertEquals("Spotted 8 jays", jay.getMessage());
}

@Test
public void testInternationalizedMessage()
{
assertEquals("Welcome to DeltaSpike", this.messages.welcomeToDeltaSpike());
}

@Test
public void testInternationalizedParametrizedMessage()
{
assertEquals("Welcome to Apache DeltaSpike", this.messages.welcomeTo("Apache DeltaSpike"));
}
}
@@ -22,8 +22,8 @@
import org.apache.deltaspike.core.api.message.MessageBundle;

@MessageBundle
public interface BirdMessages
public interface SimpleMessage
{
@Message("Spotted %s jays")
String numberOfJaysSpotted(int number);
@Message("Welcome to DeltaSpike")
String welcomeToDeltaSpike();
}
@@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.deltaspike.test.core.api.message;

import org.apache.deltaspike.core.api.provider.BeanManagerProvider;
import org.apache.deltaspike.core.impl.message.MessageBundleExtension;
import org.apache.deltaspike.test.util.ArchiveUtils;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.enterprise.inject.spi.Extension;
import javax.inject.Inject;

import static org.junit.Assert.assertEquals;

/**
* Tests for {@link org.apache.deltaspike.core.api.message.Message}
*/
@RunWith(Arquillian.class)
public class SimpleMessageTest
{
@Inject
private SimpleMessage simpleMessage;

/**
* X TODO creating a WebArchive is only a workaround because JavaArchive
* cannot contain other archives.
*/
@Deployment
public static WebArchive deploy()
{
new BeanManagerProvider()
{
@Override
public void setTestMode()
{
super.setTestMode();
}
}.setTestMode();

JavaArchive testJar = ShrinkWrap
.create(JavaArchive.class, "messageTest.jar")
.addPackage("org.apache.deltaspike.test.core.api.message")
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");

return ShrinkWrap
.create(WebArchive.class, "messageTest.war")
.addAsLibraries(ArchiveUtils.getDeltaSpikeCoreArchive())
.addAsLibraries(testJar)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsServiceProvider(Extension.class,
MessageBundleExtension.class);
}

@Test
public void testSimpleMessage()
{
assertEquals("Welcome to DeltaSpike", this.simpleMessage.welcomeToDeltaSpike());
}
}
@@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.deltaspike.test.core.api.message;

import org.apache.deltaspike.core.api.message.MessageInterpolator;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class TestMessageInterpolator implements MessageInterpolator
{
@Override
public String interpolate(String messageText, Object[] arguments)
{
return String.format(messageText, arguments);
}
}
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.deltaspike.test.core.api.message;

import org.apache.deltaspike.core.api.message.Message;
import org.apache.deltaspike.core.api.message.MessageBundle;
import org.apache.deltaspike.core.api.message.MessageContextConfig;

@MessageBundle
@MessageContextConfig(messageInterpolator = TestMessageInterpolator.class)
public interface TestMessages
{
@Message("Spotted %s jays")
String numberOfJaysSpotted(int number);

@Message("{welcome_to_deltaspike}")
String welcomeToDeltaSpike();

@Message("{welcome_to}")
String welcomeTo(String name);

//X TODO
//@Message("{welcome_to}")
//String welcomeTo(MessageContext messageContext, String name);
}
@@ -0,0 +1,19 @@
#Licensed to the Apache Software Foundation (ASF) under one
#or more contributor license agreements. See the NOTICE file
#distributed with this work for additional information
#regarding copyright ownership. The ASF licenses this file
#to you under the Apache License, Version 2.0 (the
#"License"); you may not use this file except in compliance
#with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing,
#software distributed under the License is distributed on an
#"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
#KIND, either express or implied. See the License for the
#specific language governing permissions and limitations
#under the License.

welcome_to=Welcome to %s
welcome_to_deltaspike=Welcome to DeltaSpike
@@ -138,6 +138,7 @@
</goals>
<configuration>
<includes>**/*Test.class</includes>
<includes>**/*.properties</includes>
<excludes>org.apache.deltaspike.test.core.api.projectstage.*</excludes>
<artifactItems>
<artifactItem>